python——多线程系列爬取妹子图!!!

项目分析

在python环境下使用多线程对妹子图网站的爬取;

爬取目标

爬取网站里面各个小组的图片,保存到本地;

使用工具

  1. python3.5;
  2. vscode;
  3. win10;

涉及模块

  • requests、beautifulsoup、time、json、os、queue、threading、random

目标分析

首先,对主链接进行请求,获取各小组图片的链接,主链接get请求,返回HTML字符串,进行初级的数据提取,接着进行下一页信息提取。
其次,获取到url_list之后,针对每一页提取到图片的链接,以及下一页的url
然后,获取到图片链接之后进行图片保存,以及下一张图片请求下载保存。
最后,将以上单线程拆分,建立三个队列,创建请求,提取,保存等功能,在主线程创造多线程,往线程内塞功能。

项目实施

代码部分

import requests
from bs4 import BeautifulSoup
import time
import json
import os
from queue import Queue
import threading
import random


class Mzitu_spider:
    def __init__(self):
        super().__init__()
        self.url = '***********'	# 爬取的主链接
        self.header = {}
        self.requests_session = requests.Session()
        # 创建队列
        self.url_queue = Queue()
        self.response_queue = Queue()
        self.img_save_queue = Queue()
        self.user_agent_list = [
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 "
            "(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
            "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 "
            "(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 "
            "(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
            "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 "
            "(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
            "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 "
            "(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 "
            "(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
            "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 "
            "(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
            "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
            "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 "
            "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 "
            "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
            "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
            "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
            "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
            "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
            "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
            "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
            "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 "
            "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
            "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
            "(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 "
            "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
            "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 "
            "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
        ]

    def update_header(self, referer):
        self.header['Referer'] = '{}'.format(referer)
        self.header['User-Agent'] = '{}'.format(
            random.choice(self.user_agent_list))

    def get_url_list(self, url, url_list=[]):
        # 获取主页各个图片组合的链接,包括名字
        # 发送请求,获取页面信息
        response = self.requests_session.get(url, headers=self.header)
        time.sleep(0.5)
        soup = BeautifulSoup(response.text, "lxml")
        tag_span = soup.find_all("ul", attrs={"id":
                                              "pins"})[0].find_all("span")
        next_url = soup.find('a', attrs={"class": "next page-numbers"})
        tag_a_list = []
        # 获取页面图片名字及链接
        for span in tag_span:
            if span.find("a") is not None:
                tag_a_list.append(span.find("a"))
        for a in tag_a_list:
            item = {}
            item['href'] = a['href']
            item['string'] = a.string
            url_list.append(item)
        print("添加成功")
        # 处理下一页
        if next_url is None:
            return url_list
        else:
            print("下一页开始!!")
            return self.get_url_list(url=next_url['href'], url_list=url_list)

    def save_url(self, url_list):
        # 保存url列表
        with open("url.txt", "w+", encoding='utf-8') as f:
            for content in url_list:
                js = json.dumps(content, ensure_ascii=False)
                f.write(js)
                f.write("\n")
            f.close()

    def process_item(self):
        # 提取数据线程
        while True:
            # 处理图片链接
            response_item = self.response_queue.get()
            soup = BeautifulSoup(response_item['response'].text, "lxml")
            img_url = soup.find("img")["src"]
            img_name = soup.find("h2", attrs={"class": "main-title"}).string
            next_url = soup.find("div", attrs={
                "class": "pagenavi"
            }).find_all("span")[-1].string
            if next_url == "下一页»":
                next_url = soup.find("div", {
                    "class": "main-image"
                }).find("a")["href"]
            else:
                next_url = None
            # 保存图片
            item = {}
            item['img_url'] = img_url
            item['img_name'] = img_name
            item['referer'] = response_item['url']
            self.img_save_queue.put(item)
            if next_url is None:
                print("一组图片下载完成")
                self.response_queue.task_done()
            else:
                # 请求下一张图片网页并获取链接
                print("开始下载下一张照片!")
                self.update_header(response_item['url'])
                response = self.requests_session.get(next_url,
                                                     headers=self.header)
                time.sleep(3)
                item = {}
                item['response'] = response
                item['url'] = next_url
                self.response_queue.put(item)
                self.response_queue.task_done()

    def get_details_page_item(self):
        # 获取详情页图片
        while True:
            url = self.url_queue.get()
            # 发送请求之前添加referer请求头
            # 发送请求,获取响应
            self.update_header(self.url)
            response = self.requests_session.get(url, headers=self.header)
            time.sleep(3)
            item = {}
            item['response'] = response
            item['url'] = url
            # 交给下一个函数处理
            # 返回图片下载链接,及下一张图片网页链接
            self.response_queue.put(item)
            self.url_queue.task_done()

    def save_img(self):
        # 保存图片
        while True:
            img_item = self.img_save_queue.get()
            suffix = os.path.splitext(img_item['img_url'])
            file_name = img_item['img_name'] + suffix[1]
            time.sleep(1)
            with open(".\\imgs\\" + file_name, "wb") as f:
                self.update_header(img_item['referer'])
                f.write(
                    self.requests_session.get(img_item['img_url'],
                                              headers=self.header).content)
                f.close()
            print("成功保存一张张片")
            self.img_save_queue.task_done()

    def run(self):
        # 图片组合的链接列表
        # 获取并保存所有图片组合的链接
        # url_list = self.get_url_list(self.url)
        # self.save_url(url_list)
        # 打开保存的链接文件
        with open(".\\url.txt", "r", encoding='utf-8') as f:
            lines = f.readlines()
            item_list = []
            for line in lines:
                dic_line = json.loads(line)
                item_list.append(dic_line)
            f.close()
        for url in item_list:
            self.url_queue.put(url['href'])
        # 调试使用
        # 可以针对某个单独的链接进行爬取
        # self.url_queue.put('https://www.mzitu.com/223616')
        # url列表提取完成
        print("*" * 30 + "准备加载图片!")
        thread_list = []
        # 发送请求获取响应
        for i in range(4):
            t_get = threading.Thread(target=self.get_details_page_item)
            thread_list.append(t_get)
        # 提取数据
        t_html = threading.Thread(target=self.process_item)
        thread_list.append(t_html)
        # 保存照片
        t_save = threading.Thread(target=self.save_img)
        thread_list.append(t_save)
        for t in thread_list:
            t.setDaemon(True)  # 把子线程设置为守护线程
            t.start()

        for q in [self.url_queue, self.response_queue, self.img_save_queue]:
            q.join()  # 让主线程堵塞,等待其他线程

        print("主线程结束!,爬取完成。")


if __name__ == '__main__':
    mzitu = Mzitu_spider()
    mzitu.run()

项目结果

项目结果这里,还是算了吧不然估计要审核不通过了,对于图片结果大家还是走代码看吧,毕竟要科学上网,文明编程嘛,哈哈!!!,主要是怕火!!!

项目总结

首先项目注意点这块,还是非常有必要说的,在写这个小项目的时候,也是遇到很多的小挫折,比如在跑程序的时候每发送一个请求都要更换请求头尤其是referer,因为在你请求图片的时候,网站会检测,如果请求头只加了一个User_Agent是不得行的,所有小编觉得不断变换请求头太麻烦就使用了requests.Session方法,当然应该也可以直接用get请求,这里我没有试过,嫌麻烦就直接用的这个方法。还有在添加线程之后最好加一些反爬信息,如果有条件直接给挂一个代理IP,因为这个网站现在开始有反爬机制了,在这里我只设置了随机User_Agent,而且在代码中我也在请求的时候让程序睡几秒,由于只是学习为目的,所以没必要让它这么快,但是如果有条件的读者可以直接挂一个代理IP接口,随心所欲啦,哈哈!!!
其次,在代码中run方法里面,先把头两行注释打开获取url_list,再接着塞到队列里面,当然也可以不用保存到本地,只需要稍改run方法即可。
最后,声明以上内容仅作为学习使用,毕竟有的爬虫还是违反道德的嘛,希望大家可以喜欢,学艺不精,还请海涵!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值