爬取的网址:http://www.doutula.com/photo/list/?page=
本次主要是学习多线程爬虫,使用queue队列下的生产者与消费者模式。
网站分析:这个网站的构造比较简单,直接在上面的网址后面加上1表示第一页,加上2表示第二页,以此类推。数据也都可以用requests直接访问到,没有什么反爬虫的措施,但是要爬的慢一点,太快了还是会出问题。
我这里使用的多线程的方法,爬取200页的表情包。具体代码如下:
主函数:首先通过Queue模块,构建一个200的队列用于存放每一页表情包的网址,用于提取每一个表情包的网址。构建一个1000的队列用于存放每一个表情包的网址,方便下载到本地。然后使用for循环,构建200页网址,并存放进page_queue队列中。最后构建五个生产者和五个消费者,并进行启动。
def main():
page_queue = Queue(200)
img_queue = Queue(1000)
url_s = "http://www.doutula.com/photo/list/?page="
for i in range(1, 201):
url = url_s + str(i)
page_queue.put(url)
for i in range(5):
t = Producter(page_queue, img_queue)
t.start()
for i in range(5):
t = Consumer(page_queue, img_queue)
t.start()
if __name__ == '__main__':
main()
生产者函数(继承Thread模块):一开始保险起见,我在构建headers请求头的时候,还是加入了很多的信息,包括User-Agent,Accept,Cookie。然后构建初始函数,并使用super来调用父类,转换对象。详细的解析,我就写在注释中了。
class Producter(threading.Thread):
Headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36",
"Accept": r"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
"Cookie": "__cfduid=d8249568756fd0d224d02db36892b6a601558100044; UM_distinctid=16ad4b7a705a24-06142f93c274de-353166-144000-16ad4b7a706a56; _ga=GA1.2.859291572.1558346967; _gid=GA1.2.1511793670.1558346967; yjs_id=3c0fb66a6388207a130a52420586feb2; ctrl_time=1; XSRF-TOKEN=eyJpdiI6IjFuS2U2eEZqM1JCNFhDaUFObkpDS0E9PSIsInZhbHVlIjoidXR0bWx6UHVNWTUxdFlyWEl6THFsXC9UVUkxODRPaVVpcEIyM1NyNDJoSU96cWc1c29VcDg5VG5vTjlZblJMTlwvIiwibWFjIjoiYzg3OGRkOTQ3YWE2Nzk1YWU4ZTdlMzMyMjE4MTNiMDMzZTY0ODdkYzA0ODc5NGUwYTM5NjBkN2ZlOGVlN2JiMiJ9; doutula_session=eyJpdiI6InRod2pjNEpXQnNcL0RKYml2dGMzb2NRPT0iLCJ2YWx1ZSI6ImlaVE8yOXhVTVNoR0ZHWVRra2RsaTJieFlhVUdBZ3BZUllzdW5OZVJCWVRDT2pJckdsT2dneWdaeDdNWDhxazEiLCJtYWMiOiIwN2RhNmE0NjFhZTJjOTRmMzdhMThkZmJhNjYzNGMxYzA1M2RjYjcwZDg1NjcxMjQ0OWU3MDEyMjI5YzM0NzQ1In0%3D; CNZZDATA1256911977=1301539121-1558343450-%7C1558358613"
}
def __init__(self, page_queue, img_queue, *args, **kwargs):
super(Producter, self).__init__(*args, **kwargs)
self.page_queue = page_queue
self.img_queue = img_queue
def run(self):
while True:
if self.page_queue.empty(): #队列为空跳出循环
break
url = self.page_queue.get() #从page_获取url
self.parse_url(url)
def parse_url(self, url): #对获取到的url进行解析
response = requests.get(url, headers=self.Headers)
html = etree.HTML(response.text)
imgs = html.xpath("//div[@class='page-content text-center']//a//img")
for img in imgs:
url_img = img.get("data-original")
alt = img.get("alt")
try:
suffix = os.path.splitext(url_img)[1] #使用os模块获取文件扩展名
except:
continue
filename = alt + suffix
self.img_queue.put((url_img, filename)) #将文件名和获取到的表情包的地址存放进img_queue队列中
time.sleep(0.5)
消费者函数(继承Thread模块):初始函数的构建和生产者一样,然后使用request模块中的urlretrieve函数将表情包下载到本地。对于表情包的名字无法构成文件名的,我这里就不要了,其实页可以使用replace函数或者re模块中的sub函数进行处理。
class Consumer(threading.Thread):
def __init__(self,page_queue, img_queue, *args, **kwargs):
super(Consumer, self).__init__(*args, **kwargs)
self.page_queue = page_queue
self.img_queue = img_queue
def run(self):
while True:
if self.page_queue.empty() and self.img_queue.empty(): #判断两个队列都为空则跳出循环
break
url_img, filename = self.img_queue.get()
try:
request.urlretrieve(url_img, r"C:\Users\juno\Desktop\PYTHOY\python3-reptlie\test/" + filename)
print(filename + "下载完成")
except FileNotFoundError:
continue
time.sleep(0.5)
最后是本次爬虫用到的库:
import requests
from lxml import etree
from urllib import request
import os
import time
import threading
from queue import Queue
爬取结果:
最后是爬了六千多个就没有爬了,晚上还要睡觉(⊙﹏⊙)。