多线程爬取斗图啦图片

爬取斗图啦的表情包对于入门的同学来说很简单,但是对于小编这种不会多线程的人来说,这是个很好的练习多线程的机会。

由于小编是在读生,所以花了两天的课余时间去做这个程序,最大的困惑就是:

为什么第一天爬取斗图啦每两页就积极拒绝我?这么容易就识别出爬虫了,然后小编就用了代理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

 

posted on 2019-03-24 19:32  佛大老妖  阅读(178)  评论(0编辑  收藏  举报

导航