简单爬虫的首选请求模块requests
By 青衣极客 Blue Geek In 2019-12-05
如果有一个需求是在网上爬去大量人物的照片,从而制作自己的人脸检测数据集,那么就需要一款好用的工具发起http请求。python自带了一个urllib的模块,虽然也可以工作,但是实在是太简陋。有一款第三方库requests,对http请求进行了直观易用的封装,直接使用requests模块能够更容易得实现所需功能。本文只是讨论一下requests模块最基本的用法,不过这个最基本的用法也足以完成一些比较有意思的网上资源抓取任务。更复杂的功能可以在掌握基本用法后,自行根据手册说明进行扩展。
1. 模块情况简介
requests库是一个第三方模块,所以需要额外安装,直接运行pip3 install requests即可。下面展示一个简单的网页抓取过程,并打印出返回结果的一些常用属性。由于默认属性不是utf-8会造成中文解析乱码,因此需要对编码进行调整。
import requests
resp = requests.get(url='http://www.baidu.com')
print('url={}'.format(resp.url))
print('status_code={}'.format(resp.status_code))
print('headers={}'.format(resp.headers))
print('cookies={}'.format(resp.cookies))
print('encoding={}'.format(resp.encoding))
resp.encoding = 'utf-8'
print('text={}'.format(resp.text))
url=http://www.baidu.com/
status_code=200
headers={'Connection': 'close', 'Transfer-Encoding': 'chunked', 'Cache-Control': 'private, no-cache, no-store, proxy-revalidate, no-transform', 'Content-Encoding': 'gzip', 'Content-Type': 'text/html', 'Date': 'Thu, 05 Dec 2019 05:31:33 GMT', 'Last-Modified': 'Mon, 23 Jan 2017 13:28:12 GMT', 'Pragma': 'no-cache', 'Server': 'bfe/1.0.8.18', 'Set-Cookie': 'BDORZ=27315; max-age=86400; domain=.baidu.com; path=/'}
cookies=<RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
encoding=ISO-8859-1
text=<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>©2017 Baidu <a href=http://www.baidu.com/duty/>使用百度前必读</a> <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a> 京ICP证030173号 <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>
使用百度搜索页面作为抓取对象,返回status_code属性的值为200表示访问成功。此外,还可以发现,返回结果中包含了headers、cookies和text属性。其中headers表明本次请求的一些信息,通常我们需要对请求进行伪装,就是通过修改headers完成的。比如有一些网站会对请求进行一些判断,如果不是浏览器或者自己的客户端发出的请求,基本可以认定为爬虫在请求网站,就可以部署一些“反爬虫”的策略。在抓取这种网站的数据时,就需要使用headers中的user-agent字段将本次请求伪装成浏览器流量。有些网站还会验证cookies的信息,比如一个需要登录才能访问的网站,需要从浏览器访问的流量中抓取出对应的cookies,并设置在requests请求中,从而完成用户验证。正常抓取下来的网页内容存储在text和content属性中,其中text存储的是文本形式的字符串,而content是存储二进制的字符串。抓取文本内容直接使用text属性即可,如果是抓取文件,则应当使用content属性。
上面演示的请求是使用GET方法获取到的,熟悉http协议的朋友应该知道http请求还有一些别的方法,比如post、put、delete、head和options等方法。其中get方法最常用,其次是post,剩下的基本不会使用。post方法一般用于提交一些表单数据,然而在稍微严谨一点的网站中,提交表单时都会使用Token,也就是说,没有Token的情况下post方法无法正常工作。因此,在实际的小批量网站抓取任务中最常用的还是get方法。网站终究是需要进行信息公开展示,也就说,我们通常看到的数据,除了表单,基本都是可以使用get方法来请求的。比如京东和淘宝的商品信息,比如知乎和微博的评论信息等等都是可以通过requests模块的get方法进行抓取的。
2. 请求图片
我们想要完成的是抓取包含人物的图像,但是由于目前隐私规则的高度敏感,这里展示的例子是抓取一个美食的图片。由于图片在网站中常常以静态文件的方法提供,我们抓取到的图片也是以二进制字节流的方法存储。这时,就不再使用text属性,而是使用content属性获取数据。除了requests模块,这里还用到了PIL模块和ByteIO模块。其中PIL在python3中需要安装pillow第三方库才能正常使用,而ByteIO是内置模块可以直接使用。
import requests
from PIL import Image
from io import BytesIO
resp = requests.get(url='https://i3.meishichina.com/attachment/magic/2019/11/26/2019112615747332533768197577.jpg')
img = Image.open(BytesIO(resp.content))
img.show()
img.save('../../output/requests.jpg')

从以上的演示结果可以看出,使用这种方式只需要几行代码就可以非常简单地抓取网络中的图像数据。如果想要抓取大量图片,可以先找到一个有大量图片的网站,然后通过这个网站上的img标签获取图像的url,完成单个页面的图片下载;再通过页面中的跳转链接完成深度搜索即可抓取多个页面中的图片文件了。如果想完成特定内容的图片抓取,那就需要多进行一些分析,比如这一类型的图片在命名上是否具有共同特征,不过更加通用的方式还是训练一个简单的内容判别模型,通过模型判断图片是否包含指定内容。总之,requests能够帮我们很容易得获取网上的各种数据资源,至于使用何种策略以及如何甄别,那就是自己各显神通的地方了。
3. Session和Cookie
有一些网站抓取时需要维持在一个会话中,requests模块也提供了这种工作模式。一般建议使用with子句来使用requests.Session(),避免因为一些疏忽或者异常而造成资源没能及时释放的麻烦。此外,cookies信息的设置也对能否成功的抓取某些网站资源至关重要。这里展示一些两种设置cookies的方法,并向一个测试网站发出请求进行测试。
import requests
import requests.cookies
with requests.Session() as sess:
# 第一种方法设置cookies
cookies = {'cookies_are':"just for testing"}
resp = sess.get(url='http://httpbin.org/cookies', cookies=cookies)
print('resp.text={}'.format(resp.text))
# 不带cookies访问
resp2 = sess.get(url='http://httpbin.org/cookies')
print('resp2.text={}'.format(resp2.text))
# 第二种方式设置cookies
jar = requests.cookies.RequestsCookieJar()
jar.set('desc', 'just for testing', domain='httpbin.org', path='/cookies')
resp = sess.get(url='http://httpbin.org/cookies', cookies=jar)
print('resp.text={}'.format(resp.text))
resp.text={
"cookies": {
"cookies_are": "just for testing"
}
}
resp2.text={
"cookies": {}
}
resp.text={
"cookies": {
"desc": "just for testing"
}
}
从以上测试可以看出,在会话中,不同请求之间的cookies是彼此独立的,不必担心请求之间的参数污染。
到此,requests模块的简单使用就讨论完毕。这里并没有直接展示使用requests模块完成某项具体的爬取任务,但是通过演示的用法应该也可以使用requests模块做一些有趣的事情。不过需要特别指出的是,爬虫在网络间爬取数据时需要遵守robot协议。不了解robot协议的朋友,建议在实现抓取功能之前,先在网上了解相应信息,并明确目标网站是允许抓取的,否则可能会面临一些法律上的麻烦。只是一般地小规模抓取不会对后端服务和商业利益造成重要影响而被忽略。

COMMENT
博客评论区功能由Github Issue提供,提交Issue时请以本文标题为话题。
"BG53-简单爬虫的首选请求模块requests"