美文网首页
不知道网页链接如何爬取数据(一)

不知道网页链接如何爬取数据(一)

作者: 小星star | 来源:发表于2021-07-07 15:58 被阅读0次

    前言:有些时候,我们在查一些比较老的数据,由于网站升级,或者改版之类的,我们无法得到我们想要数据的网址,也就无法爬取数据,下面,我将带大家一起来分析一下自己的案例。

    1. 明确需求
      背景:在网上搜自己的名字,居然找到一份文档,大概是这样的样子2015年普通高校招生录取名单公示(五) (sxkszx.cn) 然后就顺腾摸瓜,想找到全部的数据——2015年全部的数据

    2. 查找数据
      刚开始,由于找到了网站,2015年普通高校招生录取名单公示(五) (sxkszx.cn),网址是http://www.sxkszx.cn/,我想在网站搜索,但是发现网站没有搜索功能,而且,更要命的是,这个每一页,没有上一页,下一页的链接,这样我们就找不到相关联的数据。
      而且,我们发现,网址的拼接也是很奇怪,http://www.sxkszx.cn/news/2015722/n82552675.html,news代表新闻,2015722代表发布日期,后面的n82552675就很奇怪,如果改成n82552676则会404,也就是无法单纯的修改最后一位来完成地址的模拟

    3. 使用搜索引擎增强搜索力度
      现在进入了困境,我们无法在该网站查找。不过我们可是使用
      2015年普通高校招生录取名单 site:sxkszx.cn
      来进行网站的查找,这样是使用搜索引擎来进行该网站的搜索,在百度地址栏输入搜索即可。
      然后我们会找到

      image.png
      很多数据被找到了,然后我们分析一下,发现其中缺少了一些公示,这意味这数据不太完整。
    4. 网站地址分析 + 试探
      现在问题又回到了原点,我们不知道网页的网址,那么就无法爬取数据。但是,现在要比最开始好很多,因为现在除了录取名单五以外,还多了很多,以供我们分析。

      • 整理一下,来分析一下数据的异同
    image.png

    为了能够看出其中的异同,我把数据一行一行列了出来,然后行号就是公示名单的数字标号(空缺部分表示目前未知的网站链接)

    现在分析一下网址,我们发现,最后四位数字好像是递增的


    image.png

    对,这有点像是,新闻在数据库中存储的编号id,意味着这是第2669篇新闻,那么,第2670篇新闻的网址应该是怎样的呢?
    推测如下

    http://www.sxkszx.cn/news/日期/n四位不知名数字2670
    且日期应当是在2015.7.22当天,或者之后
    

    讲道理,我们这里应该要分析 四位不知名数字 的含义,或者讨论它是如何生成的,但是笔者想了很多,都没能猜到,之前猜测或者与时间/文章名字/等等,但是都不太对,总之没能猜到。

    • 由于我们没猜到,所以我们有一个最朴素的想法,我们遍历0000-9999不就可以了吗?找到那个可以正确返回的链接
      对,这个想法很正确。
    下面的这一段代码,返回了一个列表,列表中包含了从0000-9999的所有可能的网址链接
    def gene_urls():
        base_url = "http://www.sxkszx.cn/news/"
        date = "2015820"
        newsId = "2745"
        urls = []
        for i in range(1, 10000):
            s = str(i).zfill(4)
            url = base_url + date + "/n" + s + newsId + ".html"
            urls.append(url)
        return urls
    

    写到这里,大家可能觉得,这不很简单吗?直接

    for url in urls:
      response = requests.get(url)
      if response.status.code == 200:
          print url
          break
    

    但是,这是很朴素的单线程,我们做一个计算
    我们大概空缺的是

    日期     新闻id
    722      2675
    723      2676
    ...         
    83
    
             ...
             2696
    

    我们发现,一次尝试,都要花费很长的时间,如果我们想要全部试探,需要很长的时间,且,requests要不断断开,建立连接,花销很大。

    下面,我们尝试多线程。
    下面是一份很长的代码,大家不用关心具体怎么实现的,只要知道
    single_thread 代表单线程
    multi_thread 代表多线程
    即可

    # 不断尝试url,当返回不是404,则加入my_urls.txt中
    
    import requests
    import threading
    import requests.adapters
    import time
    
    base_url = "http://www.sxkszx.cn/news/"
    date = "2015722"
    newsId = "2678"
    
    # f = open("my_urls.txt", 'w+')
    
    flag = 0
    # count = 0
    
    
    def gene_urls():
        urls = []
        for i in range(1, 10000):
            s = str(i).zfill(4)
            url = base_url + date + "/n" + s + newsId + ".html"
            urls.append(url)
        return urls
    
    
    def test_url(url):
        # response = requests.get(url=url, timeout=30)
        # if response.status_code == 200:
        #     print(url)
    
        # try:
        #     with requests.get(url, timeout=5) as r:
        #         if r.status_code == 200:
        #             flag = 1
        #             print(url)
        # except:
        #     print("处理异常中...")
        #     time.sleep(5)
        global flag
        if flag == 0:
            response = None
            global count
            try:
                # 设置重连次数
                requests.adapters.DEFAULT_RETRIES = 5
                s = requests.session()
                # 设置连接活跃状态为False
                s.keep_alive = False
                response = requests.get(url, stream=False, timeout=10)
                if response.status_code == 200:
                    flag = 1
                    print(url)
                # 关闭请求  释放内存
                response.close()
                del (response)
            except Exception as indentfier:
                time.sleep(5)
    
    
    def single_thread(need_test_urls):
        for url in need_test_urls:
            global flag
            if flag == 0:
                test_url(url)
            else:
                break
    
    
    def multi_thread(need_test_urls):
        threads = []
        for url in need_test_urls:
            threads.append(
                threading.Thread(target=test_url, args=(url,))
            )
    
        for thread in threads:
            thread.start()
    
        for thread in threads:
            thread.join()
    
        print("ok")
    
    
    if __name__ == "__main__":
        u = gene_urls()
        multi_thread(u)
        # single_thread(u)
    

    但是问题到这里还没有结束,尽管编写上面的代码,已经踩了很多的坑,包括 如requests未断开;未手动断开(采用with 语句,自动断开会导致他会慢慢断开,还是会报错,max tries,服务器中止...);
    但是新的问题又来了,我发现这个代码在有的 日期 + 新闻id上可以运行出结果来,有的则不会,具体原因我也不太清楚,评论区的小伙伴有知道的,可以告诉一下我,谢谢~

    1. scrapy框架的使用
      最后的最后,我实在受不了了,我大概明白,应该是并发的时候,导致requests的连接无法断开,或者断开需要时间,但是还没有断开的这个期间,新的请求已经发送了,这样就会有一部分请求是没有效果的,也就是一部分链接被卡掉了。

    而这个,是目前的我所解决不了的。

    于是我想着试一下scrapy,因为我之前听过scrapy,但是没有用过,想着用它来解决并发问题,会不会好点。

    先说实验结果,最终成功解决了并发问题,对于每一个 日期 + 新闻id 所产生的链接列表,大概在1mins内可以得到结果,已经算是很快了。

    再说下去,就要讲scrapy框架了,但是现在我好饿,我先去吃个饭饭,宝宝饿了

    相关文章

      网友评论

          本文标题:不知道网页链接如何爬取数据(一)

          本文链接:https://www.haomeiwen.com/subject/ycyoultx.html