前两天写了一下关于糗百的爬虫,现在将代码分享给大家,有兴趣的同学可以了解一下
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()
基于多线程队列实现的爬虫,能够大大提高爬虫效率,而且其结构化运行方式,在某一个过程产生错误的情况下,并不会影响其他过程的裕兴,还可以动态的分配线程数,针某一个过程(如数据请求获取、存储等过程)增加线程,提高爬虫过程的速度。
针对以上如果有不同观点,欢迎评论哈