引言

​ 真的是论文写多了,竟然写了这样一个标题。之前的那篇《百度指数爬虫|爬虫篇(一)》介绍了百度指数爬虫的核心流程,包括如何获取密文、秘钥,如何解密。今天我们来解决掉其中的一些坑。

​ 现在我们只是有了获取数据的功能,而我们的需求比这要复杂的多,我们要爬取所有关键词、2006至今每天、全国各省市的搜索数量。这可是一个大工程,而且有很多很多的坑在等着我们!

那些踩过的坑

  • 并不是所有关键词都被录取

    百度指数收录的关键词其实是有限的,比如我在写这次的爬虫时,客户一共给了几百个关键词,最后发现其实被收录的只有十几个。其实这个跟关键词本身也有关系,明星什么的肯定是有的,太生僻的词难免会没被收录

  • 并不是所有日期都可以查询各省的搜索数量

    百度指数有一个非常重要的分界点:2011年01月01日。在这之前只能查询全国的搜索次数,并且只有PC端的数据,可能那时还没有做百度的APP吧

  • 一次只能搜索5个关键词

    百度指数是有对比多个关键词的搜索次数的功能的,这就意味着你可以一次查询多个关键词。但是数量也是有上限的,一次最多可以查5个关键词

  • 必须在登录状态下才可以使用百度指数

    百度指数必须登录才可以使用,否则会跳出让你登录的弹窗,其实就挺麻烦

  • 要能够获得每天的搜索次数,那么一次只能查300天

    百度指数不会很轻易的让你获取每天的数量的,如果你直接把时间调成2006年到现在,那么只会给你每隔几个月的总的搜索次数给你。通过实验发现,当你把时间范围调到300天以内,会返回每天的数据。这也就意味着如果你想获取每天的数据,那你就只能每300天获取一次,从2006年06月01日开始,到今天,每300天爬取一次

解决办法

  • 首先在真正爬取数据之前,先检验有没有登录,其实也就是Cookie有没有失效。判断有没有登录的方法也很简单:直接请求百度主页,如果出现了登录的超链接,意味着Cookie已经失效了,必须要更新Cookie了,好的,上代码:

    首先为整个Spider顶一个get方法,参数是url,用于请求url并返回相应数据:

    def get(self, url):
        headers['Cookie'] = self.cookie
        res = requests.get(url, headers=headers, timeout=30)
        if not res.status_code == 200:
            raise requests.Timeout
        return res.text
    

    之后我们定义is_login方法,用于判定用户是否登录。先请求百度的首页,然后使用XPath语法查看有没有登录的超链接,来判断是否已登录:

    def is_login(self):
        req = self.get("https://www.baidu.com")
        html = etree.HTML(req)
        login = html.xpath("//a[@name='tj_login']")
        return len(login) == 0
    
  • 之后我们要筛选关键词列表中被百度指数录取的关键词,方法也很简单。原理就是如果关键词没有被百度指数录取,那么返回的查询搜索次数的结果就会是一个空的字符串。所以我们就发送一个请求,看看返回解果是不是一个空字符串就好了:

    def is_keyword(self, keyword):
        result = self.get("http://index.baidu.com/api/AddWordApi/checkWordsExists?word={}".format(keyword))
        try:
            result = json.loads(result)
            data = result['data']['result']
            return len(data) == 0
        except:
            return False
    
  • 现在我们有了已经被录取的所有关键词了,然后就是构造请求队列了。为什么要构造这个东西呢?其实一般的爬虫就是发一个请求,接收到响应就好了。但是前面写了百度指数的坑,一次只能查5个关键词,一次只能查300天。随意我们就需要把关键词每5个分成一组,日期每300天分成一组。然后所有的关键词组合所有的日期组进行两两配对,最后构成我们的请求队列。先来个简单的,关键词每5个一组:

    def split_keywords(self, keywords: list) -> [list]:
        return [keywords[i * 5: (i + 1) * 5] for i in range(math.ceil(len(keywords) / 5))]
    

    是不是很简单,其实只有一行代码。然后我们来一个稍微难一点的,日期每300天分成一组:

    def get_time_range_list(self, start_date: str, end_date: str) -> list:
        date_range_list = list()
        startdate = datetime.datetime.strptime(start_date, '%Y-%m-%d')
        enddate = datetime.datetime.strptime(end_date, '%Y-%m-%d')
        while True:
            tempdate = startdate + datetime.timedelta(days=300)
            if tempdate > enddate:
                date_range_list.append((startdate, enddate))
                break
                date_range_list.append((startdate, tempdate))
                startdate = tempdate + datetime.timedelta(days=1)
                return date_range_list
    

    代码看着多,其实原理很简单。使用了Python的时间日期库datetime。先把传进来的字符串格式的开始日期和结束日期转化成日期类型。之后进入循环,每次给开始日期加上300天,直到开始日期大于结束日期为止,这样就构造出来每300天一组的日期分组了

    现在我们有了关键词分组,有了日期分组,最后就差把他们两个拼在一起了:

    def init_queue(self, start_date: str, end_date: str, keywords: list):
        self.params_queue = Queue()
        keywords_list = self.split_keywords(keywords)
        time_range_list = self.get_time_range_list(start_date, end_date)
        for start_date, end_date in time_range_list:
            for keywords in keywords_list:
                params = {
                    'keywords': keywords,
                    'start_date': start_date,
                    'end_date': end_date
                }
                self.params_queue.put(params)
    

    首先获取日期和关键词分组,然后进入循环,用一个嵌套循环,把两个分组里面的元素拼在一起

测试一下

  • 首先我们编写一段测试代码:

    import BaiduIndexSpider
    
    keyword = ['Java', 'Python', 'C', "sakdhkasdkkhk", 'PHP', 'Matlab', 'C++', 'HTML']
    spider = BaiduIndexSpider.BaiduIndexSpider(keyword)
    print("是否登陆:{}".format(spider.is_login()))
    print("*" * 20)
    real_list = list()
    for each in keyword:
        each_result = spider.is_keyword(each)
        print("{}:{}".format(each, each_result))
        if each_result:
            real_list.append(each)
    print("*" * 20)
    spider.init_queue("2006-06-01", "2008-12-31", real_list)
    queue = spider.params_queue
    while not queue.empty():
        print(queue.get())
    

    先创建BaiduIndexSpider对象,然后分别测试之前写的方法

  • 运行结果:

可以看到所有的方法都达到了预期的效果,把关键词分成了两组,第一组有Java、Python、C、PHP、Matlab,第二组有C++和HTML。日期分成了3组,第一组从2006-06-01到2007-03-28,第二组从2007-03-29到2008-11-19,最后一组从2008-11-20-2008-12-31。同时关键词分组和日期分组进行了两两配对

结语

其实今天的内容和真正的爬虫关系不大,也算是前期准备工作,不过这些也都是Python的一些语法基础,特别是datetime模块,在各种场合都会遇到。后面一一期将会是爬虫篇的最后一期,希望大家喜欢!

文章来源于互联网:百度指数爬虫|爬虫篇(二)

发表评论