爬虫笔记1-抓取
request 库
requests 模块非自带,需要单独下载。用于从 Web 抓取文件。
Response 属性
>>> import requests
>>> res = requests.get('http://www.gutenberg.org/cache/epub/1112/pg1112.txt')
>>> type(res)
<class 'requests.models.Response'>
>>> res.status_code == requests.codes.ok
True
>>> len(res.text)
178981
>>> print(res.text[:250])
The Project Gutenberg EBook of Romeo and Juliet, by William Shakespeare
This eBook is for the use of anyone anywhere at no cost and with
almost no restrictions whatsoever. You may copy it, give it away or
re-use it under the terms of the Proje
>>> res = requests.get('https://www.jianshu.com/u/de895f0d5942')
>>> res.status_code
403
get 方法返回一个 Response 对象,这个对象有如下属性:
属性 |
说明 |
---|---|
r.status_code |
HTTP 请求的返回状态,200 表示连接成功,404 表示失败 |
r.text |
HTTP 响应内容的字符串形式,即 url 对应的页面内容 |
r.encoding |
从 HTTP header 中猜测的响应内容编码方式 |
r.apparent_encoding |
从内容中分析出的响应内容编码方式(备选编码方式) |
r.content |
HTTP 响应内容的二进制形式 |
如果 Response 的 status_code 为 200,即 requests.codes.ok,表明请求下载成功。请求成功的话,下载的页面作为一个字符串存在 Response 的 text 里。
第二次去请求自己的简书页面,status_code 为 403,被服务器拒绝了。
编码
- r.encoding:如果 header 中不存在 charset,则认为编码为 ISO‐8859‐1。r.text 根据 r.encoding 显示网页内容
- r.apparent_encoding:根据网页内容分析出的编码方式,可以看作是 r.encoding 的备选
如果 r.text 为乱码,可以将 r.apparent_encoding 的值赋给 r.encoding。
连接异常
异常 |
说明 |
---|---|
requests.ConnectionError |
网络连接错误异常,如 DNS 查询失败、拒绝连接等 |
requests.HTTPError |
HTTP 错误异常 |
requests.URLRequired |
URL 缺失异常 |
requests.TooManyRedirects |
超过最大重定向次数,产生重定向异常 |
requests.ConnectTimeout |
连接远程服务器超时异常 |
requests.Timeout |
请求 URL 超时,产生超时异常 |
r.raise_for_status() |
如果不是 200,产生异常 requests.HTTPError |
使用 raise_for_status() 来检查是否成功,成功继续向下执行,失败会抛出异常。
import requests
res = requests.get('http://inventwithpython.com/page_that_does_not_exist')
try:
res.raise_for_status()
res.encoding = r.apparent_encoding
except Exception as exc:
print('There was a problem: %s' % (exc))
主要方法
主要有七个方法,这些方法的内部都是调用的 request 方法:
1. requests.request()
构造一个请求,支撑其它方法的基础方法。
requests.request(method, url, **kwargs)
- method:请求方式,对应 get/put/post 等 7 种 r = requests.request('GET', url, **kwargs) r = requests.request('HEAD', url, **kwargs) r = requests.request('POST', url, **kwargs) r = requests.request('PUT', url, **kwargs) r = requests.request('PATCH', url, **kwargs) r = requests.request('delete', url, **kwargs) r = requests.request('OPTIONS', url, **kwargs)
- url:拟获取页面的 url 链接
- **kwargs:控制访问的参数,共 13 个
- params:字典或字节序列,作为参数列表增加到 url 中 >>> kv = {'key1': 'value1', 'key2': 'value2'} >>> r = requests.request('GET', 'http://python123.io/ws', params=kv) >>> print(r.url) http://python123.io/ws?key1=value1&key2=value2
- data:字典、字节序列或文件对象,作为 Request 的内容 >>> kv = {'key1': 'value1', 'key2': 'value2'} >>> r = requests.request('POST', 'http://python123.io/ws', data=kv) >>> body = '主体内容' >>> r = requests.request('POST', 'http://python123.io/ws', data=body)
- json:JSON 格式的数据,作为 Request 的内容 >>> kv = {'key1': 'value1'} >>> r = requests.request('POST', 'http://python123.io/ws', json=kv)
- headers:字典,HTTP 定制头 >>> hd = {'user‐agent': 'Chrome/10'} >>> r = requests.request('POST', 'http://python123.io/ws', headers=hd)
- cookies:字典或 CookieJar,Request 中的 cookie
- auth:元组,支持 HTTP 认证功能
- files:字典类型,传输文件 >>> fs = {'file': open('data.xls', 'rb')} >>> r = requests.request('POST', 'http://python123.io/ws', files=fs)
8. timeout:设定超时时间,单位秒
```
>>> r = requests.request('GET', 'http://www.baidu.com', timeout=10)
```
9. proxies:字典类型,设定访问代理服务器,可以增加登录认证
```
>>> pxs = { 'http': 'http://user:pass@10.10.10.1:1234' 'https': 'https://10.10.10.1:4321' }
>>> r = requests.request('GET', 'http://www.baidu.com', proxies=pxs)
```
10. allow_redirects:True/False,默认为 True,重定向开关
11. stream:True/False,默认为 True,获取内容立即下载开关
12. verify:True/False,默认为 True,认证 SSL 证书开关
13. cert:本地 SSL 证书路径
### 2. requests.get()
获取 HTML 网页的主要方法,对应于 HTTP 的 GET。
```python
requests.get(url, params=None, **kwargs)
- url:拟获取页面的 url 链接
- params:url 中的额外参数,字典或字节流格式,可选
- **kwargs:12 个控制访问的参数
>>> kv = {'wd': 'Python'}
>>> r = requests.get("http://www.baidu.com/s", params = kv)
>>> r.status_code
200
>>> r.request.url
'http://www.baidu.com/s?wd=Python'
3. requests.head()
获取 HTML 网页头信息的方法,对应于 HTTP 的 HEAD。
requests.head(url, **kwargs)
- url:拟获取页面的 url 链接
- **kwargs:12 个控制访问的参数
>>> r = requests.head('http://httpbin.org/get')
>>> r.headers {'Content‐Length': '238', 'Access‐Control‐Allow‐Origin': '*', 'AccessControl‐Allow‐Credentials': 'true', 'Content‐Type': 'application/json', 'Server': 'nginx', 'Connection': 'keep‐alive', 'Date': 'Sat, 18 Feb 2017 12:07:44 GMT'}
>>> r.text ''
4. requests.post()
向 HTML 网页提交 POST 请求的方法,对应于 HTTP 的 POST。
requests.post(url, data=None, json=None, **kwargs)
- url:拟更新页面的 url 链接
- data:字典、字节序列或文件,Request 的内容
- json:JSON 格式的数据,Request 的内容
- **kwargs:12 个控制访问的参数
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.post('http://httpbin.org/post', data = payload)
>>> print(r.text)
{ ...
"form": {
"key2": "value2",
"key1": "value1"
},
}
向 URL POST 一个字典自动编码为 form(表单)。
>>> r = requests.post('http://httpbin.org/post', data = 'ABC')
>>> print(r.text)
{ ...
"data": "ABC"
"form": {},
}
向 URL POST 一个字符串自动编码为 data。
5. requests.put()
向 HTML 网页提交 PUT 请求的方法,对应于 HTTP 的 PUT。
requests.put(url, data=None, **kwargs)
- url:拟更新页面的 url 链接
- data:字典、字节序列或文件,Request 的内容
- **kwargs:12 个控制访问的参数
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.put('http://httpbin.org/put', data = payload)
>>> print(r.text)
{ ...
"form": {
"key2": "value2",
"key1": "value1"
},
}
6. requests.patch()
向 HTML 网页提交局部修改请求,对应于 HTTP 的 PATCH。
requests.patch(url, data=None, **kwargs)
- url:拟更新页面的 url 链接
- data:字典、字节序列或文件,Request 的内容
- **kwargs:12 个控制访问的参数
patch 方法是局部更新,假设 url 有 20 组数据,修改了一组,patch 提交修改的这一组数据即可,而 put 必须把所有 20 个字段一并提交,未提交字段将被删除。所以 patch 方法可以节省网络带宽。
7. requests.delete()
向 HTML 页面提交删除请求,对应于 HTTP 的 DELETE。
requests.delete(url, **kwargs)
- url:拟删除页面的 url 链接
- **kwargs:12 个控制访问的参数
几个方法内部都是调用 request 方法,因为安全问题,一般不允许提交信息,主要使用 get 和 head 方法获取信息。
保存网页文件
为了保存网页的 Unicode 编码,文件必须以二进制模式写,即文件打开模式需要是 wb
。
写文件主要通过 Response 的 iter_content() 方法,参数表示要读取的二进制字节数。
import requests
res = requests.get('http://www.gutenberg.org/cache/epub/1112/pg1112.txt')
res.raise_for_status() # 不捕获异常,如果请求下载失败程序终止
playFile = open('RomeoAndJuliet.txt', 'wb')
# playFile.write(res.content) 写入所有内容
for chunk in res.iter_content(100000):
playFile.write(chunk)
playFile.close()
Robots
全称 Robots Exclusion Standard,网络爬虫排除标准。
作用:网站告知网络爬虫哪些页面可以抓取,哪些不行。
形式:在网站根目录下的 robots.txt 文件。
例如京东的 robots 文件内容为:
User-agent: *
Disallow: /?*
Disallow: /pop/*.html
Disallow: /pinpai/*.html?*
User-agent: EtaoSpider
Disallow: /
User-agent: HuihuiSpider
Disallow: /
User-agent: GwdangSpider
Disallow: /
User-agent: WochachaSpider
Disallow: /
其它的 Robots 协议文件
- http://www.baidu.com/robots.txt
- http://news.sina.com.cn/robots.txt
- http://www.qq.com/robots.txt
- http://news.qq.com/robots.txt
Robots 协议是建议但非约束性,网络爬虫可以不遵守,但存在法律风险,所以还是应该遵守。除非写小程序,只是访问几次,和人类行为类似时,且不用于商业用途,可以不遵守。
实例
1. 访问亚马逊中国的一个网页
>>> import requests
>>> r = requests.get('https://www.amazon.cn/dp/B07DXS2KFG')
>>> r.status_encoding
503
响应码是 503,被拒绝访问了
>>> r.text
可以看到有 For information about migrating to our APIs refer to our Marketplace APIs at ...
,抱歉,我们只是想确认一下当前访问者并非自动程序。为了达到最佳效果,请确保您浏览器上的 Cookie 已启用。
之类的文字。
一般一是通过 Robots 协议限制访问, 二是通过 API 判断请求者是否是浏览器,如果不是就拒绝。
看一下请求头
>>> r.request.headers
{'User-Agent': 'python-requests/2.19.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
其中 User-Agent
是 python-requests
,而亚马逊拒绝了这种请求。
自定义请求头
>>> kv = {'user-agent':'Mozilla/5.0'}
>>> url = 'https://www.amazon.cn/dp/B07DXS2KFG'
>>> r = requests.get(url, headers = kv)
>>> r.status_code
200
>>> r.request.headers
{'user-agent': 'Mozilla/5.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
打印内容时有乱码,需要调整编码
>>> r.encoding
'ISO-8859-1'
>>> r.apparent_encoding
'utf-8'
>>> r.encoding = r.apparent_encoding
>>> r.text[2500:3500]
'ut type=hidden name="amzn" value="/E0+4zbLzrcBLVYh+4+P8Q==" /><input type=hidden name="amzn-r" value="/dp/B07DXS2KFG" />
<div class="a-row a-spacing-large">
<div class="a-box">
<div class="a-box-inner">
<h4>请输入您在这个图片中看到的字符:</h4>
<div class="a-row a-text-center">
<img src="https://images-na.ssl-images-amazon.com/captcha/ddwwidnf/Captcha_dnhmphvvcp.jpg">
</div>
<div class="a-row a-spacing-base">
<div class="a-row">
<div class="a-column a-span6">
<label for="captchacharacters">输入字符</label>
</div>'
2. 下载图片
import requests
import os
# 要下载的网络图片地址
url = 'https://img13.360buyimg.com/n1/jfs/t1/2058/25/3461/212622/5b99b98cE56bd6510/24950384ea0cebad.jpg'
# 图片要保存的地址
root = '/Users/silas/Downloads/'
path = root + url.split('/')[-1] # 截取 url 后的文件名
# 确保文件夹存在
if not os.path.exists(root):
os.mkdir(root)
# 文件不存在时再去下载
if not os.path.exists(path):
res = requests.get(url)
try:
res.raise_for_status()
with open(path, 'wb') as f:
f.write(res.content) # 二进制内容写到文件中
print('文件下载成功')
except:
print('爬取失败')
else:
print('文件已存在')