Allen丶咩 发表于 2020-5-27 22:03

多线程爬虫!再也不用担心没有壁纸用了!

本帖最后由 Allen丶咩 于 2020-5-28 06:05 编辑

# 多线程下载高清壁纸

- 平台 macOS
- python版本 3.7
- 第三方库 requests, lxml, unipath

- 多线程, 生产者消费者模式
- 采用 xpath 解析网页数据
- 支持选取分类
- 支持下载指定数量的图片

http://47.240.52.47:8000/640x400.gif


论坛小萌新第一次发帖, 如有错误请多多指教!

这几天逛论坛看到了@[一只码农](https://www.52pojie.cn/home.php?mod=space&uid=1215951)的帖子 (https://www.52pojie.cn/forum.php?mod=viewthread&tid=1162877&extra=page%3D1%26filter%3Dtypeid%26typeid%3D29) 突然就想要模仿写一个, 代码里面有很多东西都是借鉴了码农大神, 希望代码有疏漏的地方还请各位大神指出并提出意见!

再次感谢大佬提供的思路



代码直接贴在下面了, 运行不了就把第三方库安装一下



由于我用的MAC代码出来以后也没有在Windows平台测试, 可能存在的问题有:

1. 文件夹无法自动创建
2. 文件夹创建时的路径不对

如果遇到什么bug还请各位大佬提出来, 代码不足的地方也请多多赐教!!


*运行界面*



*文件夹*


*代码*

```
#!/usr/local/bin/python3
# -*- coding: UTF-8 –*-
import requests
import threading
from lxml import etree
from queue import Queue
from unipath import Path
import logging
import time
import os
import random

logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s', datefmt='%H:%M:%S')# 配置日志


class SpiderWP():
    def __init__(self):
      self.num = 10# 爬取图片的张数
      self.queue = Queue()# 队列
      self.BASE_URL = "http://www.netbian.com/"# 网址
      self.sort = {}# 分类对应的链接
      self.file_path = Path(Path(__file__).parent, '壁纸')# 创建的壁纸路径
      self.sort_flag = ''# 分类标记

   # 请求并返回Element
    def req(self, _url):
      rs = requests.get(_url).content.decode("gbk")
      html = etree.HTML(rs)
      return html

      # 获取分类
    def get_sort(self):
      html = self.req(self.BASE_URL)
      tags = html.xpath("//div[@class='nav cate']/a")
      for tag in tags:
            self.sort = self.BASE_URL + tag.get("href")

    # 递归生成图片的入口链接
    def get_page(self, _url):
      while True:
            html = self.req(_url)
            tags = html.xpath("//div[@class='wrap clearfix']//li/a")
            for tag in tags:
                self.get_jpg(self.BASE_URL + tag.get('href'))
            try:
                next_link = self.BASE_URL + \
                  html.xpath("//a/@href")
            except IndexError:
                logging.warning('图片已全部下载')
                self.num = 0
            if self.num != 0:
                self.get_page(next_link)
            else:
                break
   
    # 获取图片链接添加队列
    def get_jpg(self, _url):
      if self.num != 0:
            html = self.req(_url)
            try:
                title = html.xpath("//div[@class='action']//h1/text()")
                link = html.xpath("//div[@class='pic']//p//a//img/@src")
                self.queue.put((title, link))
                self.num -= 1
            except IndexError:
                logging.warning("图片解析失败,跳过...")

    # 下载图片
    def download(self):
      while True:
            if not self.queue.empty():
                title, link = self.queue.get()

                # 下载图片间隔时间,不建议太低给服务器造成压力(多线程下载的已经很快了)
                time.sleep(2)
                jpg = requests.get(link).content
                title = title + '.' + link.split(".")[-1]
                jpg_path = Path(self.jpg_path, title)

                # 如果本地已经存在同名文件,随机改名
                if jpg_path.exists():
                  jpg_path = Path(self.jpg_path, title.split('.') + str(random.randint(1, 100)) + '.' + link.split(".")[-1])
                logging.info('下载:%s', title)
                with open(jpg_path, 'wb') as f:
                  f.write(jpg)
            # 如果队列为空,则结束程序
            if self.num == 0 and self.queue.empty():
                logging.info('下载完成')
                break

    def mean(self):
      print('''日历   动漫   风景   美女   游戏   影视   动态   唯美   设计   护眼
可爱   汽车   花卉   动物   节日   人物   美食   水果   建筑   体育
军事   非主流 其他   LOL王者荣耀
-------------------------请输入分类名称-------------------------''')
      while True:
            inp = input("==>")
            if inp in self.sort:
                # 根据输入分类在‘壁纸’文件夹下创建子文件夹
                self.jpg_path = Path(self.file_path, inp)
                if not self.jpg_path.exists():
                  os.makedirs(self.jpg_path)

                self.sort_flag = inp
                #
                while True:
                  try:
                        self.num = int(
                            input('-------------------------请输入下载张数-------------------------\n==>'))
                        break
                  except ValueError:
                        print('请输入数字')
                break
            else:
                print('输入有误,请重新输入')

    def run(self):
      self.get_sort()
      self.mean()
      r = threading.Thread(target=self.get_page, args=((self.sort,)))
      r.start()

      # 多进程开启,不建议调高
      for i in range(8):
            t = threading.Thread(target=self.download)
            t.start()


a = SpiderWP()
a.run()


```

Allen丶咩 发表于 2020-5-28 05:28

fanvalen 发表于 2020-5-27 23:41
所以get要(timeout=**)

谢谢大佬, 想了好久怎么处理结束的问题, 后来是用判断剩余图片量(self.num)和判断队列是否为空来跳出循环的

Allen丶咩 发表于 2020-5-28 05:25

hj170520 发表于 2020-5-27 23:52
大体看完,都理解的。谢谢楼主
我额外又复习了一遍。
logging 这个包我还第一次看到,哈哈哈

日志调试的包, 也可以以文件的形式输出到本地

时光书窝 发表于 2020-5-27 22:17

前排顶贴!

geniusrot 发表于 2020-5-27 22:26

没看懂这爬虫的意义。。。是网站不给下载。还是不给预览?

diyikuai 发表于 2020-5-27 22:26

是高清原图吗???

Allen丶咩 发表于 2020-5-27 22:30

geniusrot 发表于 2020-5-27 22:26
没看懂这爬虫的意义。。。是网站不给下载。还是不给预览?

重点是批量

Allen丶咩 发表于 2020-5-27 22:34

diyikuai 发表于 2020-5-27 22:26
是高清原图吗???

1080x1920的

大兵马元帅 发表于 2020-5-27 22:36

[url=forum.php?mod=redirect

我也刚接触Python,说实话,应用有局限性。

geniusrot 发表于 2020-5-27 22:42

还不如爬微软的壁纸。我还挺喜欢看微软的壁纸的。

minibeetuaman 发表于 2020-5-27 23:00

多线程要处理超时的问题,不然其中一个卡主,你的程序没法结束

Zeaf 发表于 2020-5-27 23:11

如楼上所说,多线程是个问题{:1_926:}
虽然一般可能不会有啥事
页: [1] 2
查看完整版本: 多线程爬虫!再也不用担心没有壁纸用了!