爬虫 多线程 案例:爬取糗事百科

1.多线程爬虫主要分三步实现:

1.发送请求,获取响应

2.提取数据

3.保存

对于上面三部分别使用队列来实现,分为URL队列,响应队列,数据队列。

2.为什么使用URL队列?

在单线程中,我们只需要从列表中一条一条的读取URL,在多个线程中,如果同时读URL列表,结果会是多个线程读取相同的URL地址,访问相同的数据,所以引入URL队列(先入先出的性质)。

多线程实现的流程图如下:

3.多线程爬虫代码(爬取糗事百科)


import requests
from lxml import etree
from queue import Queue
import threading

class QiubaiSpider:
    def __init__(self):
        self.url_temp = "https://www.qiushibaike.com/text/page/{}/"
        self.headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"}
        self.url_queue = Queue()
        self.html_queue = Queue()
        self.content_queue = Queue()

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

    def parse_url(self):
       
        while True:
            url = self.url_queue.get()

            print(url)
            response = requests.get(url,headers=self.headers)
            self.html_queue.put(response.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)
            div_list = html.xpath("//div[@id='content-left']/div")
            content_list = []
            for div in div_list:
                item = {}
                item["id"] = div.xpath(".//h2/text()")
                item["id"] = [i.replace("\n", "") for i in item["id"]]
                item["content"] = div.xpath(".//div[@class='content']/span/text()")
                item["content"] = [i.replace("\n", "") for i in item["content"]]
                item["author_image"] = div.xpath(".//div/a/img/@src")
                item["author_image"] = "https:"+item["author_image"][0] if len(item["author_image"]) > 0 else None
                item["age"] = div.xpath(".//div[contains(@class, 'articleGender')]/text()")
                item["age"] = item["age"] if len(item["age"]) > 0 else None
                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
                item["stats_vote"] = div.xpath(".//div/span/i/text()")
                item["stats_vote"] = item["stats_vote"][0] if len(item["stats_vote"]) > 0 else None
                item["user_commons"] = div.xpath(".//span[@class='stats-comments']/a/i/text()")
                item["user_commons"] = item["user_commons"][0] if len(item["stats_vote"]) > 0 else None
                content_list.append(item)
            # return content_list
            self.content_queue.put(content_list)
            self.html_queue.task_done()


    # 打印内容
    def save_content_list(self):

        while True:
            content_list = self.content_queue.get()
            for i in content_list:
                # print(i)
                pass
            self.content_queue.task_done()

    def run(self):
        thread_list = []
        # 1.url_list
        t_url = threading.Thread(target=self.get_url_list)
        thread_list.append(t_url)
        # 2.遍历,发送请求,获取响应
        for i in range(20):
            t_parse = threading.Thread(target=self.parse_url)
            thread_list.append(t_parse)
        # 3.提取数据
        for i in range(2):
            t_html = threading.Thread(target=self.get_content_list)
            thread_list.append(t_html)
        # 4.保存
        t_save = threading.Thread(target=self.save_content_list)
        thread_list.append(t_save)
        for t in thread_list:
            t.setDaemon(True)  # 把子线程设置为守护线程,该线程不重要主线程结束,子线程结束
            t.start()

        for q in [self.url_queue, self.html_queue, self.content_queue]:
            q.join()  # 让主线程等待阻塞,等待队列的任务完成之后再完成

        print("主线程结束")

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



多线程代码思路:首先导入thread,queue的包,定义了3个队列,分别存放 [网站地址,网页代码,网页提取的数据]。

随后将url一个一个put进去,在parse()中一个一个get,最后,将得到解析后的dict类型数据一个一个打印出来,注意,每次从队列中get数据,在程序结束后需要执行task_down结束进程,以上只是队列处理数据的流程。

接下来介绍多线的实现,在run方法中定义一个thread列表,将每一个队列加入进程,根据需要增加进程数,将进程设置为守护进程,启动进程,最后设置主线程等待阻塞。

猜你喜欢

转载自blog.csdn.net/Rand_C/article/details/86605789