多线程爬取斗图啦图片
爬取斗图啦的表情包对于入门的同学来说很简单,但是对于小编这种不会多线程的人来说,这是个很好的练习多线程的机会。
由于小编是在读生,所以花了两天的课余时间去做这个程序,最大的困惑就是:
为什么第一天爬取斗图啦每两页就积极拒绝我?这么容易就识别出爬虫了,然后小编就用了代理IP
然鹅,到了第二天,一用代理IP就拒绝,用自己的IP去爬取的话。。。爬了几百页都没问题
当然了,代理IP用的是西刺上的,质量难说,但是我弄个了代理IP池,程序用的IP都是验证过的,所以很迷。。
下面附上源码:
import requests from bs4 import BeautifulSoup import threading import random import re import os # 全局变量统一大写! PAGE_URL = 'https://www.doutula.com/photo/list/?page=' # 某一页的url PAGE_LIST = [PAGE_URL + str(i) for i in range(101,1001)] IMAGE_URL=[] #保存图片URL IMAGE_NAME=[] #保存图片名称 PAGE_LIST1=[] #保存临时页数! #全局锁 # gLock=threading.RLock() condition = threading.Condition() #条件变量 #创建3个消费者线程下载图片 def download_img(): # global PAGE_LIST #这个代码中不用全局变量,因为PAGE_LIST没有同名的局部变量! while True: if condition.acquire(): if len(IMAGE_URL)>0: #只要还有没下载的URL #请求资源,锁住资源,锁的顺序很重要! name1 = IMAGE_NAME.pop() page1 = PAGE_LIST1.pop() url1=IMAGE_URL.pop() condition.notify() #随机通知一个线程进入锁定池 condition.wait() #进入等待池,调用这个方法将使线程进入Condition的等待池等待通知,并释放锁 page = re.compile('[0-9]+').findall(page1)[0] #获得页数,当作次一级文件名 print(page) for i,j in zip(url1,name1): i=requests.get(i).content #请求资源,获得图片内容content dir='D:/doutu1/'+str(page)+'' if os.path.exists(dir) == False: # 如果不存在文件夹名为该文件夹名,就创建 os.mkdir(dir) try: #可能存在非法字符 with open('D:/doutu1/'+str(page)+'/'+str(j)+'.jpg','wb')as file: file.write(i) except: with open('D:/doutu1/'+str(page)+'/'+str(random.randint(1,100))+'.jpg','wb')as file: file.write(i) else: condition.notify() condition.wait()#进入等待池,调用这个方法将使线程进入Condition的等待池等待通知,并释放锁 condition.release() if len(PAGE_LIST)==0: os._exit(0) #当没有需要爬取的页面时就退出程序 #创建2个生产者线程来爬取表情的url def run(): # global PAGE_LIST while True: if condition.acquire(): #把资源锁住,不让其他线程使用 if len(PAGE_LIST)>0: #只要还有可以爬取的页面就生产url page_url=PAGE_LIST.pop() #真实页数url,通过pop方法,因为多线程,哪一页不确定 rsp = requests.get(page_url, verify=False) # 请求某一页数据 # img_html=etree.HTML(rsp.text) #解析网页数据 img_html = BeautifulSoup(rsp.text, 'lxml') # print(img_html) img_list = img_html.find_all('img', attrs={'class': 'img-responsive lazy image_dta'}) # 由于网络不可能一直很流畅,src不会那么快加载,可能出现白茫茫表情,所以抓取data-original,这样就指向真实网址 img_url = [i['data-original'] for i in img_list] img_namelist = img_html.find_all('p', attrs={'style': 'display: none'}) img_name = [i.text for i in img_namelist] print(img_url) # 添加进列表,锁住资源 PAGE_LIST1.append(page_url) IMAGE_URL.append(img_url) IMAGE_NAME.append(img_name) condition.notify() condition.wait() else: condition.notify() # 随机通知一个线程进入锁定池 condition.wait() # 进入等待池 condition.release() #内部循环结束,锁释放 def main(): #创建2个生产者下载表情url for i in range(2): th1=threading.Thread(target=run) th1.start() # 创建4个消费者线程下载图片 for j in range(4): th2=threading.Thread(target=download_img) th2.start() if __name__=='__main__': main()
1.斗图啦这个程序是看视频学的,重点看多线程部分,链接是:https://study.163.com/course/courseLearn.htm?courseId=1005001016#/learn/video?lessonId=1051195841&courseId=1005001016
2.视频上使用的是LOCK去锁住资源,程序中用的是condition变量,大家需要先用程序实现生产者消费者模型再理解源码好点,附上学习链接:
https://www.cnblogs.com/tkqasn/p/5700281.html
https://www.cnblogs.com/Unikfox/p/9704207.html