爬虫基本原理

爬虫

爬虫就是一个请求网站并提取指定数据的程序,相当于代替手动操作浏览器访问网址并获取需要信息的过程。

上网基本流程

浏览器输入网址 –> 回车(向目标URL发送一个HTTP请求) –> 看到网页(服务器响应请求,把代码返回给浏览器解析成看到的页面

  • 本质是网络通信,即通过网络进行数据传递
  • 浏览器经过通信后获取到该页面的源代码文档(HTML等)
  • 浏览器解析文档后以适当的形式展现给用户

爬虫基本流程

前面说爬虫相当于代替手动操作浏览器访问网址,所以,爬虫的过程也相当于一个上网的过程。

发送请求

通过HTTP向目标站点发送一个Request请求,请求可以包含额外的headers等信息。

获取响应内容

服务器正常响应后,会得到一个Response,响应包括目标页面的内容,如HTML文档、Json字符串或二进制数据(如图片、视频)等

处理内容

可以通过正则表达式或其他网页解析库进行网页解析,然后提取出自己需要的内容进行保存,如保存到Excel、数据库等。

Request

浏览器访问一个网址,就是向服务器发送Request的过程。

Request包含:

  • 请求方法及URL:主要有GETPOST
  • 请求头:包含许多有关的客户端环境和请求正文的有用信息,如User-Agentcookies
  • 请求体:请求携带的额外信息,如POST请求发送的表单数据

Response

服务器接收Request通过后台代码进行处理,生成相应Response返回给浏览器。

Response包含:

  • 响应状态:状态码及相应的状态消息。200表示成功,其他的还有404500
  • 响应头:包含一些服务器信息,内容类型等许多内容。
  • 响应体:包含了请求的资源,如HTML文档、css和其他二进制数据等

实例

访问百度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
In [1]: import requests

In [2]: headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrom
...: e/63.0.3239.132 Safari/537.36'}

In [3]: response = requests.get('http://baidu.com', headers = headers)

In [4]: print(response.status_code)
200

In [5]: print(response.headers)
{'Bdpagetype': '1', 'Bdqid': '0xa246c16500007bf8', 'Bduserid': '0', 'Cache-Control': 'private', 'Connection': 'Keep-Alive', 'Content-Encoding': 'gzip', 'Content-Type': 'text/html; charset=utf-8', 'Cxy_all': 'baidu+f8cc963045aca76a5e616434e2aa01cb', 'Date': 'Sun, 25 Feb 2018 08:11:54 GMT', 'Expires': 'Sun, 25 Feb 2018 08:11:47 GMT', 'P3p': 'CP=" OTI DSP COR IVA OUR IND COM "', 'Server': 'BWS/1.1', 'Set-Cookie': 'BAIDUID=3F56499DC023D4234F4419B30E63B960:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com, BIDUPSID=3F56499DC023D4234F4419B30E63B960; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com, PSTM=1519546314; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com, BDSVRTM=0; path=/, BD_HOME=0; path=/, H_PS_PSSID=25640_1441_21114_17001_22158; path=/; domain=.baidu.com', 'Strict-Transport-Security': 'max-age=172800', 'Vary': 'Accept-Encoding', 'X-Powered-By': 'HPHP', 'X-Ua-Compatible': 'IE=Edge,chrome=1', 'Transfer-Encoding': 'chunked'}

In [6]: print(response.text)
<!DOCTYPE html>
<!--STATUS OK-->
...
...
</body>
</html>

requests.get()就是发送Request请求并把获取到服务器返回的Response赋值给response,接下来就可以对得到的请求进行操作了:

  • response.status_code打印出状态码。
  • response.headers打印出响应头。
  • response.text打印出源码。

获取其他类型数据

除了文本数据,还可以获取到图片:

1
2
3
4
5
6
7
8
9
10
11
In [7]: response = requests.get('https://www.bing.com/az/hprichbg/rb/WoolBaySeadragon_ZH-CN13348117046_1920x1080.jpg')

In [8]: print(response.content)
b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x01\x00\x00\x00\x00\x00\x00\xff\xdb\x00C\x00\x06\x04\x04\x04\x04\x04\x06\x04\x04\x06\x08\x05\x05\x05\x08\n\x07\x06\x06\x07\n\x0b\t\t\n\t\t\x0b\x0e\x0b\x0c\x0c\x0c\x0c\x0b\x0e\x0c\r\r\x0e\r\r\x0c\x11\x11\x12\x12\x11\x11\x19\x18\x18\x18\x19\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\xff\xdb\x00C\x01\x06\x06\x06\x0b\n\x0b\x15\x0e\x0e\x15\x17\x13\x10\x13\x17\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c...
...

In [9]: with open('E:/Notes/img.jpg', 'wb') as f:
...: f.write(response.content)
...:

In [10]:

可以看到response.content就是图片的二进制流数据,可以通过操作文件的方法保存图片。

进阶

有时候,我们通过上面的方法获取的内容和我们在浏览器看到的内容是不一样的,这是因为我们看到的内容是js渲染过以后的内容,是通过Ajax获取后台数据在前端显示的。

这时候常用的方法就是:

  • 分析Ajax请求。可以借助浏览器开发者工具或者Fiddler等抓包工具。
  • 使用selenium
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    In [7]: import selenium

    In [8]: from selenium import webdriver

    In [10]: driver = webdriver.Chrome()

    DevTools listening on ws://127.0.0.1:12591/devtools/browser/518f43b6-3307-43df-83da-547e126a7d65

    In [11]: driver.get("http://www.baidu.com")

    In [12]: driver.page_source
    Out[12]: '<!DOCTYPE html><!--STATUS OK--><html xmlns="http://www.w3.org/1999/xhtml"><head>\n \n <meta http-equiv="content-type" content="text/html;charset=utf-8" />\n <meta http-equiv="X...
    ...

这时候的源码就是渲染之后的了。