python爬虫爬取图片

python爬虫简单基础

使用Request库

优点:简单、发送网络请求快

缺点:容易被反爬策略针对

request库的使用

携带Headers去发送网络请求

模拟浏览器环境、欺骗服务器、获取和浏览器一致的内容

比较重要的就是ua

1
2
3
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}

request.get(url,headers=headers)

发送携带参数的请求

1
2
kw = {'name':'yueyun','age':18}
request.get(url,params=kw)

在 url 地址中,很多参数是没有用的,比如百度搜索的 url 地址,其中参数只有一个字段有用,其他的都可以删除

发送post请求

  • 登录注册
  • 需要传输大文本内容的时候
1
2
3
4
5
data = {
"name": "yueyun",
"age": 18
}
response = requests.post("http://www.baidu.com/", data = data,headers=headers)

使用代理

使用代理原因:

  • 让服务器以为不是同一个客户端在请求
  • 隐藏真实地址

正向代理和反向代理:

  • 正向代理:即是**”代理服务器”代理了”客户端”,去和”目标服务器”进行交互**,例如VPN
  • 反向代理:代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。例如Nginx

代理使用:

1
2
3
4
5
proxies = { 
"http": "http://127.0.0.1:7890",
"https": "https://127.0.0.1:7890",
}
requests.get("http://www.baidu.com",proxies=proxies)

代理 IP 使用的注意点

  • 反反爬
    使用代理 ip 是非常必要的一种反反爬的方式,但是即使使用了代理 ip,对方服务器任然会有很多的方式来检测我们是否是一个爬虫

    • 一段时间内,检测 IP 访问的频率,访问太多频繁会屏蔽
    • 检查 Cookie,User-Agent,Referer 等 header 参数,若没有则屏蔽
    • 服务方购买所有代理提供商,加入到反爬虫数据库里,若检测是代理则屏蔽

    所以更好的方式是购买质量更高的代理,或者自己搭建代理服务器,组装自己的代理IP池,同时在使用的时候使用随机的方式进行选择使用,不要每次都用一个代理 ip,没事没有任何效果的

  • 代理IP池更新

使用Request处理cookie等相关的请求

cookie和session的区别

  • cookie 数据存放在客户的浏览器上,session 数据放在服务器上。
  • cookie 不是很安全,别人可以分析存放在本地的 cookie 并进行 cookie 欺骗。
  • session 会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能。
  • 单个 cookie 保存的数据不能超过 4K,很多浏览器都限制一个站点最多保存 20 个 cookie。

处理cookie请求方式 - session

  • Requests提供了一个叫做session类,来实现客户端和服务端的会话保持

  • 会话保持有两个内涵:

    • 保存 cookie
    • 实现和服务端的长连接
  • 使用方法

    1
    2
    session = requests.session()
    response = session.get(url,headers)

    session 实例在请求了一个网站后,对方服务器设置在本地的 cookie 会保存在 session 中,下一次再使用 session 请求对方服务器的时候,会带上前一次的 cookie

处理cookie请求方式 - headers

headers中的cookie:

  • 使用分号 (;) 隔开
  • 分号两边的类似 a=b 形式的表示一条 cookie
  • a=b 中,a 表示键(name),b 表示值(value)
  • headers 中仅仅使用了 cookie 的 name 和 value

cookie 的具体组成的字段

由于 headers 中对 cookie 仅仅使用它的 name 和 value,所以在代码中我们仅仅需要 cookie 的 name 和 value 即可

1
2
3
4
5
headers = {
"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
"Cookie":" Pycharm-26c2d973=dbb9b300-2483-478f-9f5a-16ca4580177e; Hm_lvt_98b9d8c2fd6608d564bf2ac2ae642948=1512607763; Pycharm-26c2d974=f645329f-338e-486c-82c2-29e2a0205c74; _xsrf=2|d1a3d8ea|c5b07851cbce048bd5453846445de19d|1522379036"}

requests.get(url,headers=headers)

cookie 有过期时间,所以直接复制浏览器中的 cookie 可能意味着下一程序继续运行的时候需要替换代码中的 cookie,对应的我们也可以通过一个程序专门来获取 cookie 供其他程序使用;当然也有很多网站的 cookie 过期时间很长,这种情况下,直接复制 cookie 来使用更加简单

处理cookie请求方式 - 使用 cookies 参数

1
2
cookies = {"cookie的name":"cookie的value"}
requests.get(url,headers=headers,cookies=cookie_dict}

requests.utils.dict_from_cookiejar: 把 cookiejar 对象转化为字典

1
2
3
4
5
6
7
8
import requests

url = "http://www.baidu.com"
response = requests.get(url)
print(type(response.cookies))

cookies = requests.utils.dict_from_cookiejar(response.cookies)
print(cookies)

Request常见参数

ssl证书

1
2
3
4
5
6
7
import requests

url = "https://www.12306.cn/mormhweb/"

# ssl.CertificateError ...
# 添加false
response = requests.get(url,verify=False)

超时参数

1
response = requests.get(url,timeout=3)

retrying 模块的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# parse.py
import requests
from retrying import retry

headers = {}


@retry(stop_max_attempt_number=3) #最大重试3次,3次全部报错,才会报错
def _parse_url(url)
response = requests.get(url, headers=headers, timeout=3) #超时的时候回报错并重试
assert response.status_code == 200 #状态码不是200,也会报错并充实
return response


def parse_url(url)
try: #进行异常捕获
response = _parse_url(url)
except Exception as e:
print(e)
response = None
return response

使用无头浏览器selenium

数据提取

案例

爬取 vilipix上面的图片

需要使用到的依赖:

  • pip install requests
  • pip install pyquery
  • pip install fake-useragent
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
import requests
from pyquery import PyQuery as pq
from fake_useragent import UserAgent
import os
import datetime
from urllib.parse import urljoin
from concurrent.futures import ThreadPoolExecutor

# 随机请求头
ua = UserAgent(verify_ssl=False, path='./fake_useragent0.1.11.json')
# 网站url
base_url = 'https://www.vilipix.com'
# 获取当前日期
today = datetime.date.today()
# 获取昨天的日期,并用于构建url
today_str = (datetime.date.today() + datetime.timedelta(days=-1)).strftime('%Y%m%d')

mark = int(input("请选择要下载的榜单\n0---每日榜单\n1---每周榜单\n2---每月榜单\n"))
mode = ''
if (mark == 0):
mode = 'daily'
elif mark == 1:
mode = 'weekly'
elif mark == 2:
mode = 'monthly'
else:
print("输入有误,即将退出程序")
os._exit(0)
# 分布创建属于榜单的文件夹(可以自定义更改)
path_1 = f'D:/vilipix{mode}榜单'
if not os.path.exists(path_1):
os.mkdir(path_1)

path_2 = f'D:/vilipix{mode}榜单/{today}/'
if not os.path.exists(path_2):
os.mkdir(path_2)

# 随机请求头防止被封
def ua_random():
headers = {
'use_agent': ua.random
}
return headers

# 返回网页内容
def scrap_page(url):
try:
response = requests.get(url=url, headers=ua_random())
if response.status_code == 200:
response.encoding = 'utf-8'
return response.text
except requests.RequestException:
print(f'{url}不可爬取!')


# 返回具体的url地址
def scrap_index(page):
url = f'{base_url}/ranking?date={today_str}&mode={mode}&p={page}'
return scrap_page(url)


# 对页面进行解析
def parse_index(html):
doc = pq(html)
# pQuery 和 web开发中jQuery 差不多 CSS选择器
links = doc('#__layout .illust-content li .illust a')
for link in links.items():
# 获取link标签的href属性
href = link.attr('href')
name = href.split('/')[-1] # 详情页名字,由图片id构成,以防重名
# 详情页url 拼接
detail_url = urljoin(base_url, href)
page_count = link('.page-count span').text()
# 惰性生成器
yield detail_url, page_count, name

# 下载图片 保存至本地文件
def download(path, name, image):
save_path = path + name + '.jpg'
with open(save_path, 'wb') as f:
f.write(image)


# 详情页内仅有一张图片时调用
def detail_index_1(html, name, path):
doc = pq(html)
link = doc('.illust-pages li a img').attr('src')
image = requests.get(url=link, headers=ua_random()).content
download(path, name, image)


# 详情页内有超过一张图片时调用
def detail_index_more(html, name, path):
doc = pq(html)
links = doc('.illust-pages li a img')
i = 1
for link in links.items():
src = link.attr('src')
image_name = name + f'_{i}'
image = requests.get(url=src, headers=ua_random()).content
download(path, image_name, image)
i += 1

# 下载程序入口
def main(page):
html = scrap_index(page)
details = parse_index(html)
for detail in details:
detail_url = detail[0] # 详情页的url
num = detail[1] # 详情页内图片的数量
name = detail[2] # 给详情页命的名
detail_html = scrap_page(detail_url)
if num == '1': # 第①种情况
detail_index_1(detail_html, name, path_2)
else: # 第②种情况
path_3 = f'D:/vilipix{mode}榜单/{today}/{name}/'
if not os.path.exists(path_3):
os.mkdir(path_3)
detail_index_more(detail_html, name, path_3)
print('*'*10, f'{name}下载完毕!', '*'*10)
# print("图片下载完成辣,谢谢使用!!")

## 主程序入口
if __name__ == '__main__':
pages = list(range(1, 15))
# 使用多线程进行加速
with ThreadPoolExecutor(max_workers=5) as executor:
executor.map(main, pages)
print("图片下载完成辣,谢谢使用!!")