糗事百科爬虫实现

前两天写了一下关于糗百的爬虫,现在将代码分享给大家,有兴趣的同学可以了解一下

1,下面是正常流程实现爬虫过程的源代码,如下:

# coding=utf-8
import requests
import json
from lxml import etree

class QiubaiSpider:
    def __init__(self):
        self.part_url = 'https://www.qiushibaike.com'
        self.session = requests.session()  # 我们需要的是存在上次请求的cookie,而不是新的cookie,但是这里仍然会存在一
        # 个问题,当大量访问的时候,对方会识别出来
        # 所以这个session定义在全局的位置
        self.headers = self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0)\
         Gecko/20100101 Firefox/63.0'}
        self.url_temp = "https://www.qiushibaike.com/hot/page/{}/"

    def get_url_list(self):
        #方法一
        # url_list = []
        # for i in range(1,14):
        #     url_list.append(self.url_temp.format(i))

        # 方法二,列表推导时
        url_list = [self.url_temp.format(i) for i in range(1,14)]
        return url_list
        # 也可以写成return [self.url_temp.format(i) for i in range(1,14)]

    def parse_url(self, url):
        print(url)
        # self.session = requests.session()  # session不在这里,因为我们不希望没一次访问都使用新的cookie
        # html_str = session.get(url, headers=self.headers)
        html_str = requests.get(url, headers=self.headers)
        return html_str.content.decode()

    def get_content_list(self, html_str):
        html = etree.HTML(html_str)  # 利用etree.HTML,将字符串转化为Element对象,这一条语句写在处理数据的方法中比较合理
        div_list = html.xpath("//div[@id='content-left']/div[contains(@class,'article')]")  # 这里实现对每一天
        # 糗百的分组
        content_list = []
        for div in div_list:
            item = {}
            item["author_log"] = div.xpath("./div[contains(@class,'author')]//img/@src")[0]
            # print(item["author_log"])
            item["author_name"] = div.xpath("./div[contains(@class,'author')]//h2/text()")[0].strip()
            # print(item["author_name"])
            item["author_gender"] = div.xpath(".//div[contains(@class,'articleGender')]/@class")
            item["author_gender"] = item["author_gender"][0].split(" ")[-1].replace("Icon","") if len(item["author_gender"]) > 0 else None
            # print(item["author_gender"])
            item["author_age"] = div.xpath(".//div[contains(@class,'articleGender')]/text()")
            item["author_age"] = item["author_age"][0] if len(item["author_age"]) > 0 else None
            # print(item["author_age"])
            item["content"] = div.xpath(".//div[@class='content']/span/text()")
            item["content"] = item["content"][0].strip() if len(item["content"]) > 0 else None
            # print(item["content"])
            item["content_img"] = div.xpath(".//div[@class='thumb']/a/@href")
            item["content_img"] =self.part_url + item["content_img"][0] if len(item["content_img"]) > 0 else None
            # print(item["content_img"])
            item["好笑"] = div.xpath(".//span[@class='stats-vote']/i[@class='number']/text()")[0]
            # print(item["好笑"])
            item["评论"] = div.xpath(".//span[@class='stats-comments']//i[@class='number']/text()")[0]
            # print(item["评论"])
            content_list.append(item)
        return content_list
# 注意:对于不确定的地方,需要判断其师傅存在值。为空的话很可能会报错

    def save_content_list(self, content_list):
        with open("qiubaispider.txt", "a", encoding="utf-8") as f:
            for content in content_list:
                f.write(json.dumps(content, ensure_ascii=False, indent=2))
                f.write("\n")
        print("保存成功")
    def run(self):  # 实现主要逻辑
        # 获取url列表
        url_list = self.get_url_list()
        # 遍历发送请求,获取响应
        for url in url_list:
            html_str = self.parse_url(url)
            # 处理数据
            content_list = self.get_content_list(html_str)
            # 保存数据
            self.save_content_list(content_list)

if __name__ == '__main__':
    qiubai_spider = QiubaiSpider()
    qiubai_spider.run()

其中,主要过程都使用自定义函数来实现的。另外的,在排错过程中,很多地方可能目测并不能发现具体错误信息,于是会在可产报错的语句旁边增加结果打印语句或者try except语句来查看具体报错的结果

2、下面是基于队列的多线程改造的代码

# coding=utf-8
import requests
import json
from lxml import etree
from queue import Queue
from threading import Thread

class QiubaiSpider:
    def __init__(self):
        self.part_url = 'https://www.qiushibaike.com'
        self.session = requests.session()  # 我们需要的是存在上次请求的cookie,而不是新的cookie,但是这里仍然会存在一
        # 个问题,当大量访问的时候,对方会识别出来
        # 所以这个session定义在全局的位置
        self.headers = self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0)\
         Gecko/20100101 Firefox/63.0'}
        self.url_temp = "https://www.qiushibaike.com/hot/page/{}/"
        self.url_queue = Queue()
        self.html_queue = Queue()
        self.content_list_queue = Queue()

    def get_url_list(self):
        for i in range(1,14):
            self.url_queue.put(self.url_temp.format(i))

    def parse_url(self):
        while True:
            url = self.url_queue.get()
            print(url)
            html_str = requests.get(url, headers=self.headers)
            self.html_queue.put(html_str.content.decode())
            self.url_queue.task_done()

    def get_content_list(self):
        while True:
            html_str = self.html_queue.get()
            html = etree.HTML(html_str)  # 利用etree.HTML,将字符串转化为Element对象,这一条语句写在处理数据的方法中比较合理
            div_list = html.xpath("//div[@id='content-left']/div[contains(@class,'article')]")  # 这里实现对每一天
            # 糗百的分组
            content_list = []# 注意:对于不确定的地方,需要判断其师傅存在值。为空的话很可能会报错
            for div in div_list:
                item = {}
                item["author_log"] = div.xpath("./div[contains(@class,'author')]//img/@src")[0]
                # print(item["author_log"])
                item["author_name"] = div.xpath("./div[contains(@class,'author')]//h2/text()")[0].strip()
                # print(item["author_name"])
                item["author_gender"] = div.xpath(".//div[contains(@class,'articleGender')]/@class")
                item["author_gender"] = item["author_gender"][0].split(" ")[-1].replace("Icon","") if len(item["author_gender"]) > 0 else None
                # print(item["author_gender"])
                item["author_age"] = div.xpath(".//div[contains(@class,'articleGender')]/text()")
                item["author_age"] = item["author_age"][0] if len(item["author_age"]) > 0 else None
                # print(item["author_age"])
                item["content"] = div.xpath(".//div[@class='content']/span/text()")
                item["content"] = item["content"][0].strip() if len(item["content"]) > 0 else None
                # print(item["content"])
                item["content_img"] = div.xpath(".//div[@class='thumb']/a/@href")
                item["content_img"] =self.part_url + item["content_img"][0] if len(item["content_img"]) > 0 else None
                # print(item["content_img"])
                item["好笑"] = div.xpath(".//span[@class='stats-vote']/i[@class='number']/text()")[0]
                # print(item["好笑"])
                item["评论"] = div.xpath(".//span[@class='stats-comments']//i[@class='number']/text()")[0]
                # print(item["评论"])
                content_list.append(item)
            self.content_list_queue.put(content_list)
            self.html_queue.task_done()

    def save_content_list(self):
        while True:
            content_list = self.content_list_queue.get()
            with open("qiubaispider_Thread.txt", "a", encoding="utf-8") as f:
                for content in content_list:
                    f.write(json.dumps(content, ensure_ascii=False, indent=2))
                    f.write("\n")
            print("保存成功")
            self.content_list_queue.task_done()

    def run(self):  # 实现主要逻辑
        thread_list = []
        # 获取url列表
        t_get_url = Thread(target=self.get_url_list)
        thread_list.append(t_get_url)
        # 遍历发送请求,获取响应
        for i in range(2):
            t_parse_url = Thread(target=self.parse_url)
            thread_list.append(t_parse_url)
            # 处理数据
        for i in range(6):
            t_content = Thread(target=self.get_content_list)
            thread_list.append(t_content)
            # 保存数据
        for i in range(6):
            t_save = Thread(target=self.save_content_list)
            thread_list.append(t_save)
        for t in thread_list:
            t.setDaemon(True)  # 把子线程设置为守护线程,该线程不重要,主线程结束,子线程结束,这里设置守护进程要设置在
            # t.start()前面,不然会设置失败(都运行起来了),然后也不需要t.join()等待线程执行完成的阻塞函数了
            t.start()
        for q in [self.url_queue, self.html_queue, self.content_list_queue]:
            q.join()  # 让主线程阻塞,等待所有队列的任务完成
        print("主线程结束")

if __name__ == '__main__':
    qiubai_spider = QiubaiSpider()
    qiubai_spider.run()

基于多线程队列实现的爬虫,能够大大提高爬虫效率,而且其结构化运行方式,在某一个过程产生错误的情况下,并不会影响其他过程的裕兴,还可以动态的分配线程数,针某一个过程(如数据请求获取、存储等过程)增加线程,提高爬虫过程的速度。

针对以上如果有不同观点,欢迎评论哈

猜你喜欢

转载自blog.csdn.net/u010199356/article/details/85318742
今日推荐