loading...
爬虫日记-一键下载****张壁纸-升级版版 彼岸 桌面壁纸 附源码 单文件一键可执行
Published in:2022-01-05 | category: 爬虫日记
Words: 3.2k | Reading time: 16min | reading:

1
2
3
4
5
6
7
8
9
10
11
环境
window10

python3

re
requests
etree
threading
threadpool
redis

[TOC]

源码地址

前言

这次的升级版主要修复了上个程序的一些不足的地方,并且是为了用到之前总结过的redis只是而特意去应用,有一些应试教育的感觉。

image-20211229161558586

程序整体流程

流程图如下

image-20211229163925581

程序开始进行一次初始化,主要是是将任务写进到redis中,判断img文件夹是否存在,不存在就原地新建一个。

之后开启一个线程,从redis去获取每一个网页url,判断一下历史记录中有没有处理过这个url,没有就处理,处理完就写进历史。

然后用线程池去下载图片,同样在本地文件夹进行判断是否存在。

程序解说

环境

python环境

这里用到的包有:

1
2
3
4
5
6
7
8
import os.path
import random
import re
import threading
import time
import requests
import redis
from lxml import etree

都是常见的包,没有什么稀奇古怪的,使用pycharm的话,直接把鼠标放在导包出现红色的地方,然后点安装软件包即可。

image-20211229170916927

使用其他的可以使用pip在命令行中安装,示例如下:

1
pip install lxml

image-20211229171103205

编码申明

Python中默认的编码格式是 ASCII 格式,在没修改编码格式时无法正确打印汉字,所以在读取中文时会报错。

解决办法:在文件开头指定编码格式

1
2
3
# -*- coding: UTF-8 -*-
# 或者
# coding=utf-8

注:一定是第一行呀。

程序初始化

根据 http://www.netbian.com/index.htm 网站每一页的url规则,将要采集的url添加到redis中。

这个程序下载图片的位置是本地同级目录img,所以先建一个这样的文件夹。(当然要是本来就有那就不用程序去建)

1
2
3
4
5
6
7
8
9
10
11
12
# 初始化
with open('task.txt', 'w', encoding='utf-8') as f:
f.writelines('http://www.netbian.com/index.htm' + '\n')
# 当前网站最多页为,1223,即下方的1000最大可以替换成1224
for i in range(2, 1000):
f.writelines("http://www.netbian.com/index_{}.htm".format(i) + '\n')
if not os.path.exists('./img'):
os.mkdir('./img')
# 代理线程,定时更新代理
proxy_time_task(20)
time.sleep(3)
print("初始化结束")

访问列表页获取图片名

这边主要是从本地文件读取任务及历史,如果这一页有历史数据,那么就跳过,否则就采集对应的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def get_source_code(url):
# 构造一个普通的请求头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36',
}
# 发送请求,拿到数据
# 因为代理的问题,多次尝试
for i in range(20):
proxy = random.choice(proxy_list) if proxy_list else None
try:
if proxy:
if 'https' in proxy:
proxies = {'https': proxy}
else:
proxies = {'http': proxy}
print("尝试第{}次访问\t{}".format(i + 1, url))
response = requests.request("GET", url, headers=headers, timeout=3,
proxies=proxies)
if response.status_code == 200:
print("访问通过---")
break
else:
print("直接访问")
response = requests.request("GET", url, headers=headers)
except:
pass
if response:
# 返回网页源码
text = response.content.decode('gbk')
data_list = re.findall("<img src=\"http://img.netbian.com/file/\d{4}/\d{4}/small.*?\.jpg\" alt=\".*?\" />",
text)
for data in data_list:
try:
small_pic_url = re.findall("src=\"(.*?)\"", data)[0]
pic_id = re.search(r"/small(.*?)\.jpg", data).groups()[0][:-10]
pic_url = re.sub(re.findall("/(small.*?)\.jpg", small_pic_url)[0], pic_id, small_pic_url)
pic_name = re.findall("alt=\"(.*?)\"", data)[0]
threading.Thread(target=deonload_img, args=(pic_name, pic_url)).start()
except:
print("出现意外。。。")
if url + '\n' not in history:
with open('history.txt', 'a+', encoding='utf-8') as f:
f.writelines(url + '\n')

下载图片

和之前的方法一样,使用request直接写入本地,但是这次加上了文件名。

有些东西是为了用到所学而故意为之,让整个程序变成了能用的屎山。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
def deonload_img(img_name, img_url):
global proxy_list
if os.path.exists('img/' + str(img_name) + '.jpg'):
print("已存在,pass")
return
else:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36',
}
# 发送请求,拿到数据
# 因为代理质量差,多次尝试
for i in range(20):
try:
proxy = random.choice(proxy_list) if proxy_list else None
if proxy:
# print("使用代理", proxy)
# if 'https' in proxy:
# proxies = {'https': proxy}
# else:
# proxies = {'http': proxy}
print("尝试第{}次下载图片\t{}".format(i + 1, img_name))
resp = requests.request("GET", img_url, headers=headers, timeout=3,
proxies={'http': proxy, 'https': proxy.replace("http", "https")})
if resp.status_code == 200:
open('img/' + str(img_name) + '.jpg', 'wb').write(resp.content) # 将内容写入图片
print("下载图片成功", img_name)
break
else:
print("直接访问")
resp = requests.get(img_url, headers=headers, stream=True)
if resp.status_code == 200:
open('img/' + str(img_name) + '.jpg', 'wb').write(resp.content) # 将内容写入图片
print("下载图片成功", img_name)
return
except:
pass
with open('img_download_fail.txt', 'a+', encoding='utf-8') as f:
f.writelines(img_name + ' ' + img_url + '\n')

代理相关

这里使用 小幻HTTP代理 ,主要是看它上面对代理的更新和检测的相对够快。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def get_proxy_list():
url = "https://ip.ihuan.me/address/5Lit5Zu9.html"
payload = {}
headers = {
'authority': 'ip.ihuan.me',
'sec-ch-ua': '"Chromium";v="94", "Google Chrome";v="94", ";Not A Brand";v="99"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36',
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'sec-fetch-site': 'same-origin',
'sec-fetch-mode': 'navigate',
'sec-fetch-user': '?1',
'sec-fetch-dest': 'document',
'accept-language': 'zh-CN,zh;q=0.9'
}
response = requests.request("GET", url, headers=headers, data=payload)
res = []
_ = etree.HTML(response.text)
type_dct = {
"HTTP": "http://",
"HTTPS": "https://"
}
data_list = _.xpath("//tbody/tr")
for data in data_list:
ip = data.xpath("./td[1]/a/text()")[0]
port = data.xpath("./td[2]/text()")[0]
type = "HTTP"
res.append(type_dct[type] + ip + ':' + port)
return res


# 测试代理
def check(proxy):
href = 'https://www.baidu.com'
# href = 'http://img.netbian.com/file/2022/0103/224706qcowj.jpg'
if 'https' in proxy:
proxies = {'https': proxy}
else:
proxies = {'http': proxy}
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4396.0 Safari/537.36'
}
try:
r = requests.get(href, proxies=proxies, timeout=1, headers=headers)
if r.status_code == 200:
return True
except:
return False


def get_proxy():
global proxy_list
p_list = get_proxy_list()
pp_list = []
count = 0
for p in p_list:
if check(p):
pp_list.append(p)
count += 1
proxy_list = pp_list[:]
print("更新了{}个代理".format(count))

完整程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# -*- coding:utf-8 -*-
import os.path
import random
import re
import threading
import time
import requests
import redis
from lxml import etree

proxy_list = []


def get_source_code(url):
# 构造一个普通的请求头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36',
}
# 发送请求,拿到数据
# 因为代理的问题,多次尝试
for i in range(20):
proxy = random.choice(proxy_list) if proxy_list else None
try:
if proxy:
if 'https' in proxy:
proxies = {'https': proxy}
else:
proxies = {'http': proxy}
print("尝试第{}次访问\t{}".format(i + 1, url))
response = requests.request("GET", url, headers=headers, timeout=3,
proxies=proxies)
if response.status_code == 200:
print("访问通过---")
break
else:
print("直接访问")
response = requests.request("GET", url, headers=headers)
except:
pass
if response:
# 返回网页源码
text = response.content.decode('gbk')
data_list = re.findall("<img src=\"http://img.netbian.com/file/\d{4}/\d{4}/small.*?\.jpg\" alt=\".*?\" />",
text)
for data in data_list:
try:
small_pic_url = re.findall("src=\"(.*?)\"", data)[0]
pic_id = re.search(r"/small(.*?)\.jpg", data).groups()[0][:-10]
pic_url = re.sub(re.findall("/(small.*?)\.jpg", small_pic_url)[0], pic_id, small_pic_url)
pic_name = re.findall("alt=\"(.*?)\"", data)[0]
threading.Thread(target=deonload_img, args=(pic_name, pic_url)).start()
except:
print("出现意外。。。")
if url + '\n' not in history:
with open('history.txt', 'a+', encoding='utf-8') as f:
f.writelines(url + '\n')


def deonload_img(img_name, img_url):
global proxy_list
if os.path.exists('img/' + str(img_name) + '.jpg'):
print("已存在,pass")
return
else:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36',
}
# 发送请求,拿到数据
# 因为代理质量差,多次尝试
for i in range(20):
try:
proxy = random.choice(proxy_list) if proxy_list else None
if proxy:
# print("使用代理", proxy)
# if 'https' in proxy:
# proxies = {'https': proxy}
# else:
# proxies = {'http': proxy}
print("尝试第{}次下载图片\t{}".format(i + 1, img_name))
resp = requests.request("GET", img_url, headers=headers, timeout=3,
proxies={'http': proxy, 'https': proxy.replace("http", "https")})
if resp.status_code == 200:
open('img/' + str(img_name) + '.jpg', 'wb').write(resp.content) # 将内容写入图片
print("下载图片成功", img_name)
break
else:
print("直接访问")
resp = requests.get(img_url, headers=headers, stream=True)
if resp.status_code == 200:
open('img/' + str(img_name) + '.jpg', 'wb').write(resp.content) # 将内容写入图片
print("下载图片成功", img_name)
return
except:
pass
with open('img_download_fail.txt', 'a+', encoding='utf-8') as f:
f.writelines(img_name + ' ' + img_url + '\n')


def get_proxy_list():
url = "https://ip.ihuan.me/address/5Lit5Zu9.html"
payload = {}
headers = {
'authority': 'ip.ihuan.me',
'sec-ch-ua': '"Chromium";v="94", "Google Chrome";v="94", ";Not A Brand";v="99"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36',
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'sec-fetch-site': 'same-origin',
'sec-fetch-mode': 'navigate',
'sec-fetch-user': '?1',
'sec-fetch-dest': 'document',
'accept-language': 'zh-CN,zh;q=0.9'
}
response = requests.request("GET", url, headers=headers, data=payload)
res = []
_ = etree.HTML(response.text)
type_dct = {
"HTTP": "http://",
"HTTPS": "https://"
}
data_list = _.xpath("//tbody/tr")
for data in data_list:
ip = data.xpath("./td[1]/a/text()")[0]
port = data.xpath("./td[2]/text()")[0]
type = "HTTP"
res.append(type_dct[type] + ip + ':' + port)
return res


# 测试代理
def check(proxy):
href = 'https://www.baidu.com'
# href = 'http://img.netbian.com/file/2022/0103/224706qcowj.jpg'
if 'https' in proxy:
proxies = {'https': proxy}
else:
proxies = {'http': proxy}
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4396.0 Safari/537.36'
}
try:
r = requests.get(href, proxies=proxies, timeout=1, headers=headers)
if r.status_code == 200:
return True
except:
return False


def get_proxy():
global proxy_list
p_list = get_proxy_list()
pp_list = []
count = 0
for p in p_list:
if check(p):
pp_list.append(p)
count += 1
proxy_list = pp_list[:]
print("更新了{}个代理".format(count))


def proxy_time_task(inc):
try:
get_proxy()
except:
print("获取代理出现错误,稍后重试。")
t = threading.Timer(inc, proxy_time_task, (20,))
t.start()


if __name__ == '__main__':
# 初始化
with open('task.txt', 'w', encoding='utf-8') as f:
f.writelines('http://www.netbian.com/index.htm' + '\n')
# 当前网站最多页为,1223,即下方的1000最大可以替换成1224
for i in range(2, 1000):
f.writelines("http://www.netbian.com/index_{}.htm".format(i) + '\n')
if not os.path.exists('./img'):
os.mkdir('./img')
# 代理线程,定时更新代理
proxy_time_task(20)
time.sleep(3)
print("初始化结束")

with open('task.txt', 'r', encoding='utf-8') as f:
# 读取任务
task_list = f.readlines()
# 读取历史
if not os.path.exists('history.txt'):
history = []
else:
with open('history.txt', 'r', encoding='utf-8') as f:
history = f.readlines()
# 开始任务
for url in task_list:
url = url.replace('\r', '').replace('\n', '').strip()
if url + '\n' not in history:
get_source_code(url)
time.sleep(2)
print(url, "done!")
else:
# 如果是历史数据,那么就跳过
print("已存在,跳过")

效果

写在最后

这个爬虫有很多不完善的地方,比如:

代理质量差,毕竟是免费代理;

列表页顺序执行,下载图片多线程,如果直接关闭程序会丢失一部分图片数据,(把history删掉让程序重新开始可以解决,就是有点慢);

如果代理速度足够满,会导致线程越来越多;

页数比较后面的列表页,图片的连接格式好像有变化,但是这个不重要了,我又不是为了采集壁纸;

巴拉巴拉。。。

爬虫仅做学习交流使用。

总之这个爬虫还有很大的优化空间,下次抽点时间完善一下。

实力有限,才疏学浅,如有错误,欢迎指正。

Prev:
爬虫日记-熙攘中文网
Next:
踩坑 redis 做临时缓存、数据库使用
catalog
catalog