爬虫基本原理

爬虫是什么

img

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
#1、什么是互联网?
互联网是由网络设备(网线,路由器,交换机,防火墙等等)和一台台计算机连接而成,像一张网一样。

#2、互联网建立的目的?
互联网的核心价值在于数据的共享/传递:数据是存放于一台台计算机上的,而将计算机互联到一起的目的就是为了能够方便彼此之间的数据共享/传递,否则你只能拿U盘去别人的计算机上拷贝数据了。

#3、什么是上网?爬虫要做的是什么?
我们所谓的上网便是由用户端计算机发送请求给目标计算机,将目标计算机的数据下载到本地的过程。
#3.1 只不过,用户获取网络数据的方式是:
浏览器提交请求->下载网页代码->解析/渲染成页面。

#3.2 而爬虫程序要做的就是:
模拟浏览器发送请求->下载网页代码->只提取有用的数据->存放于数据库或文件中
 
#3.1与3.2的区别在于:
我们的爬虫程序只提取网页代码中对我们有用的数据

#4、总结爬虫
#4.1 爬虫的比喻:
如果我们把互联网比作一张大的蜘蛛网,那一台计算机上的数据便是蜘蛛网上的一个猎物,而爬虫程序就是一只小蜘蛛,沿着蜘蛛网抓取自己想要的猎物/数据

#4.2 爬虫的定义:
向网站发起请求,获取资源后分析并提取有用数据的程序

#4.3 爬虫的价值:
互联网中最有价值的便是数据,比如天猫商城的商品信息,链家网的租房信息,雪球网的证券投资信息等等,这些数据都代表了各个行业的真金白银,可以说,谁掌握了行业内的第一手数据,谁就成了整个行业的主宰,如果把整个互联网的数据比喻为一座宝藏,那我们的爬虫课程就是来教大家如何来高效地挖掘这些宝藏,掌握了爬虫技能,你就成了所有互联网信息公司幕后的老板,换言之,它们都在免费为你提供有价值的数据。

爬虫的基本流程

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#1、发起请求
使用http库向目标站点发起请求,即发送一个Request
Request包含:请求头、请求体等

#2、获取响应内容
如果服务器能正常响应,则会得到一个Response
Response包含:html,json,图片,视频等

#3、解析内容
解析html数据:正则表达式,第三方解析库如Beautifulsoup,pyquery等
解析json数据:json模块
解析二进制数据:以b的方式写入文件

#4、保存数据
数据库
文件

请求与响应

img

1
2
3
4
5
#http协议:http://www.cnblogs.com/linhaifeng/articles/8243379.html#Request:用户将自己的信息通过浏览器(socket client)发送给服务器(socket server)

#Response:服务器接收请求,分析用户发来的请求信息,然后返回数据(返回的数据中可能包含其他链接,如:图片,js,css等)

#ps:浏览器在接收Response后,会解析其内容来显示给用户,而爬虫程序在模拟浏览器发送请求然后接收Response后,是要提取其中的有用数据。

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
39
40
#1、请求方式:
常用的请求方式:GET,POST
其他请求方式:HEAD,PUT,DELETE,OPTHONS

ps:用浏览器演示get与post的区别,(用登录演示post)

post与get请求最终都会拼接成这种形式:k1=xxx&k2=yyy&k3=zzz
post请求的参数放在请求体内:
可用浏览器查看,存放于form data内
get请求的参数直接放在url后

#2、请求url
url全称统一资源定位符,如一个网页文档,一张图片
一个视频等都可以用url唯一来确定

url编码
https://www.baidu.com/s?wd=图片
图片会被编码(看示例代码)


网页的加载过程是:
加载一个网页,通常都是先加载document文档,
在解析document文档的时候,遇到链接,则针对超链接发起下载图片的请求

#3、请求头
User-agent:请求头中如果没有user-agent客户端配置,
服务端可能将你当做一个非法用户
host
cookies:cookie用来保存登录信息

一般做爬虫都会加上请求头


#4、请求体
如果是get方式,请求体没有内容
如果是post方式,请求体是format data

ps:
1、登录窗口,文件上传等,信息都会被附加到请求体内
2、登录,输入错误的用户名密码,然后提交,就可以看到post,正确登录后页面通常会跳转,无法捕捉到post
1
2
3
4
5
6
7
8
9
10
11
12
from urllib.parse import urlencode
import requests

headers={
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Cookie':'H_WISE_SIDS=101556_115474_115442_114743_108373_100099_115725_106201_107320_115339_114797_115553_116093_115546_115625_115446_114329_115350_114275_116040_110085; PSTM=1494300712; BAIDUID=42FE2934E37AF7AD1FA31D8CC7006D45:FG=1; BIDUPSID=2996557DB2710279BD865C50F9A68615; MCITY=-%3A; __cfduid=da9f97dea6458ca26aa4278280752ebb01508939712; BDSFRCVID=PGLsJeCCxG3wt_3ZUrBLDfv2D_qBZSjAgcEe3J; H_BDCLCKID_SF=tJAOoCLytI03qn5zq4Oh-4oHhxoJq5QxbT7Z0l8KtfcNVJQs-lCMhbtp-l3GJPoLWK6hBKQmWIQHDnbsbq0M2tcQXR5-WROCte74KKJx-4PWeIJo5tKh04JbhUJiB5OLBan7Lq7xfDDbbDtmej_3-PC3ql6354Rj2C_X3b7EfKjIOtO_bfbT2MbyeqrNQlTkLIvXoITJQD_bEP3Fbfj2DPQ3KabZqjDjJbue_I05f-oqebT4btbMqRtthf5KeJ3KaKrKW5rJabC3hPJeKU6qLT5Xjh6B5qDfyDoAbKOt-IOjhb5hMpnx-p0njxQyaR3RL2Kj0p_EWpcxsCQqLUonDh8L3H7MJUntKjnRonTO5hvvhb6O3M7-XpOhDG0fJjtJJbksQJ5e24oqHP-kKPrV-4oH5MQy5toyHD7yWCvjWlT5OR5Jj6KMjMkb3xbz2fcpMIrjob8M5CQESInv3MA--fcLD2ch5-3eQgTI3fbIJJjWsq0x0-jle-bQypoa-U0j2COMahkMal7xO-QO05CaD53yDNDqtjn-5TIX_CjJbnA_Hn7zepoxebtpbt-qJJjzMerW_Mc8QUJBH4tR-T3keh-83xbnBT5KaKO2-RnPXbcWjt_lWh_bLf_kQN3TbxuO5bRiL66I0h6jDn3oyT3VXp0n54nTqjDHfRuDVItXf-L_qtDk-PnVeUP3DhbZKxtqtDKjXJ7X2fclHJ7z-R3IBPCD0tjk-6JnWncKaRcI3poiqKtmjJb6XJkl2HQ405OT-6-O0KJcbRodobAwhPJvyT8DXnO7-fRTfJuJ_DDMJDD3fP36q4QV-JIehmT22jnT32JeaJ5n0-nnhP3mBTbA3JDYX-Oh-jjRX56GhfO_0R3jsJKRy66jK4JKjHKet6vP; ispeed_lsm=0; H_PS_PSSID=1421_24558_21120_17001_24880_22072; BD_UPN=123253; H_PS_645EC=44be6I1wqYYVvyugm2gc3PK9PoSa26pxhzOVbeQrn2rRadHvKoI%2BCbN5K%2Bg; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598',
'Host':'www.baidu.com',
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'}

# response=requests.get('https://www.baidu.com/s?'+urlencode({'wd':'美女'}),headers=headers)
response=requests.get('https://www.baidu.com/s',params={'wd':'美女'},headers=headers) #params内部就是调用urlencode
print(response.text)

Response

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#1、响应状态
200:代表成功
301:代表跳转
404:文件不存在
403:权限
502:服务器错误

#2、Respone header
set-cookie:可能有多个,是来告诉浏览器,把cookie保存下来

#3、preview就是网页源代码
最主要的部分,包含了请求资源的内容
如网页html,图片
二进制数据等

总结

1
2
3
4
5
6
7
8
9
10
#1、总结爬虫流程:
爬取--->解析--->存储

#2、爬虫所需工具:
请求库:requests,selenium
解析库:正则,beautifulsoup,pyquery
存储库:文件,MySQL,Mongodb,Redis

#3、爬虫常用框架:
scrapy

爬取校花网视频

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
import re
import time
import hashlib

def get_page(url):
print('GET %s' %url)
try:
response=requests.get(url)
if response.status_code == 200:
return response.content
except Exception:
pass

def parse_index(res):
obj=re.compile('class="items.*?<a href="(.*?)"',re.S)
detail_urls=obj.findall(res.decode('gbk'))
for detail_url in detail_urls:
if not detail_url.startswith('http'):
detail_url='http://www.xiaohuar.com'+detail_url
yield detail_url

def parse_detail(res):
obj=re.compile('id="media".*?src="(.*?)"',re.S)
res=obj.findall(res.decode('gbk'))
if len(res) > 0:
movie_url=res[0]
return movie_url


def save(movie_url):
response=requests.get(movie_url,stream=False)
if response.status_code == 200:
m=hashlib.md5()
m.update(('%s%s.mp4' %(movie_url,time.time())).encode('utf-8'))
filename=m.hexdigest()
with open(r'./movies/%s.mp4' %filename,'wb') as f:
f.write(response.content)
f.flush()


def main():
index_url='http://www.xiaohuar.com/list-3-{0}.html'
for i in range(5):
print('*'*50,i)
#爬取主页面
index_page=get_page(index_url.format(i,))
#解析主页面,拿到视频所在的地址列表
detail_urls=parse_index(index_page)
#循环爬取视频页
for detail_url in detail_urls:
#爬取视频页
detail_page=get_page(detail_url)
#拿到视频的url
movie_url=parse_detail(detail_page)
if movie_url:
#保存视频
save(movie_url)


if __name__ == '__main__':
main()


#并发爬取
from concurrent.futures import ThreadPoolExecutor
import queue
import requests
import re
import time
import hashlib
from threading import current_thread

p=ThreadPoolExecutor(50)

def get_page(url):
print('%s GET %s' %(current_thread().getName(),url))
try:
response=requests.get(url)
if response.status_code == 200:
return response.content
except Exception as e:
print(e)

def parse_index(res):
print('%s parse index ' %current_thread().getName())
res=res.result()
obj=re.compile('class="items.*?<a href="(.*?)"',re.S)
detail_urls=obj.findall(res.decode('gbk'))
for detail_url in detail_urls:
if not detail_url.startswith('http'):
detail_url='http://www.xiaohuar.com'+detail_url
p.submit(get_page,detail_url).add_done_callback(parse_detail)

def parse_detail(res):
print('%s parse detail ' %current_thread().getName())
res=res.result()
obj=re.compile('id="media".*?src="(.*?)"',re.S)
res=obj.findall(res.decode('gbk'))
if len(res) > 0:
movie_url=res[0]
print('MOVIE_URL: ',movie_url)
with open('db.txt','a') as f:
f.write('%s\n' %movie_url)
# save(movie_url)
p.submit(save,movie_url)
print('%s下载任务已经提交' %movie_url)
def save(movie_url):
print('%s SAVE: %s' %(current_thread().getName(),movie_url))
try:
response=requests.get(movie_url,stream=False)
if response.status_code == 200:
m=hashlib.md5()
m.update(('%s%s.mp4' %(movie_url,time.time())).encode('utf-8'))
filename=m.hexdigest()
with open(r'./movies/%s.mp4' %filename,'wb') as f:
f.write(response.content)
f.flush()
except Exception as e:
print(e)

def main():
index_url='http://www.xiaohuar.com/list-3-{0}.html'
for i in range(5):
p.submit(get_page,index_url.format(i,)).add_done_callback(parse_index)


if __name__ == '__main__':
main()