python爬虫入门篇了解
1. 爬虫分类:
1.1 通用爬虫:例如搜索引擎:无差别的收集数据;提取存储关键字;构建索引库;给用户提供搜索接口。
1.2 聚焦爬虫:有针对性的编写特定领域数据的爬取程序。
2. Robots协议:
指定一个robots.txt文件,告诉爬虫引擎什么可以爬取,什么不可以爬取。君子协议,不受法律保障。
例如:http://www.taobao.com/robots.txt
3. Http请求与响应:
爬取网页就是通过http协议访问网页,通过浏览器是人的行为,将这种行为变成程序行为,就是爬虫。
python里提供了urllib包来处理url,访问网页:
urllib.request # 打开和读写url
urllib.error # 由urllib.request引起的异常
urllib.parse # 解析url
urllib.robotparser # 分析robots.txt文件
(python2里提供了urllib和urllib2:urllib提供较为底层的接口,urllib2对urllib进行了进一步的封装;python3中将urllib和urllib2合并,并只提供了标准库urllib包。)
3.1 urllib.request模块
身份验证、重定向、cookies等应用中打开url(主要是http)的函数和类。
urlopen方法:建立http连接,获取数据。
urlopen(url, data=None)
# url是链接地址字符串或请求对象;data是提交的数据。如果data为None则发起GET请求,如果为非None,发起POST请求;返回http.client.HTTPResponse类的响应对象,是一个类文件对象。
#GET方法:数据通过URL传递,放在HTTP报文的header部分;POST方法:数据放在HTTP报文的body中提交。数据都是键值对形式,多个参数之间通过'&'连接,url和数据之间通过'?'连接。
from urllib.request import urlopen # 打开url返回一个响应对象,类文件对象 response = urlopen('http://www.bing.com') # data为None,所以是get方法 print(response.closed) # False。类似于文件对象,不会自动关闭 with response: print(type(response)) # <class 'http.client.HTTPResponse'> print(response.status) # 状态码200,表示成功。 print(response.reason) # OK print(response._method) # 请求方法:get。_method表示是内部方法 print(response.geturl()) # http://cn.bing.com/?setmkt=zh-CN&setmkt=zh-CN。'http://www.bing.com'链接跳转至的真正url print(response.info()) # 返回headers的信息 print(response.read()) # 读取返回的网页内容;字节类型 print(response.closed) # True。说明with方法同文件对象一样会自动关闭。
简述上述过程:通过urllib.request.urlopen方法,发起一个HTTP的GET请求,WEB服务器返回网页内容。响应的数据被封装到类文件对象(response)中。可以通过read、readline、deadlines等方法获取数据,status和reason属性表示返回的状态,info方法返回头信息等。
上述代码可以获得网站的响应数据,但是urlopen方法只能传递url和data参数,不能构造http请求。在python的源码中,User-agent值为Python-urllib/3.6(3.6为对应的python版本号),因此就能被一些反爬虫的网站识别出来。于是需要更改User-agent值来伪装成浏览器:赋值浏览器的UA值。
Request方法:构建request请求对象,可以修改请求头信息。
Request(url, data=None, headers={})
from urllib.request import Request, urlopen import random url = 'http://www.bing.com/' # 要访问的url;实际上是自动跳转 301 302 # 不同浏览器的User-Agent值。切换浏览器的不同的用户代理,查看网络下标头里user-agent值 ua_list = ['Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36', 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Win64; x64; Trident/6.0)', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Safari/605.1.15', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299' ] # ua要添加到请求头中 ua = random.choice(ua_list) # request = Request(url, headers={'User-agent': ua}) # 实现的功能跟下面两行代码是一样的 request = Request(url) request.add_header('User-Agent', ua) print(type(request)) # <class 'urllib.request.Request'> response = urlopen(request, timeout=20) # urlopen传入的参数可以是url对象,也可以是request对象 print(type(response)) # <class 'http.client.HTTPResponse'> with response: print(response.status) # 200 print(response.getcode()) # 200 print(response.reason) # OK print(response.geturl()) # http://cn.bing.com/ print(response.info()) # header里的信息:包含Cache-Control,Content-Length,Set-Cookies,Connection等字段信息 print(response.read()) # 网页html内容 print(request.get_header('User-agent')) # Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Win64; x64; Trident/6.0);伪装的浏览器ua值 print('user-agent'.capitalize()) # User-agent;User-agent首字母要大些
3.2 urllib.parse模块
对url进行编码和解码。
urlencode方法:url编码:主要是对post或get方法请求传递的参数编码,与原url结合,使其成为能够被访问的url格式。
urlencode(data) # 传入的data参数需要是字典或者二元组序列
from urllib.parse import urlencode u1 = urlencode({'name': 'danni', 'age': 23}) print(u1) # name=danni&age=23;get方法下u的值是以?连接放在url后,post方法下u的值是放在body里 u2 = urlencode({'test': 'http://www.taobao.com?name=danni&age=23'}) print(u2) # test=http%3A%2F%2Fwww.taobao.com%3Fname%3Ddanni%26age%3D23
从的运行结果我们可以发现:
1.斜杠、&、冒号、等号、问号等都被全部编码了(%之后实际上是单字节十六进制表示的值)。 因为提交的数据中,可能会像上述情况一样有斜杠、等号、问号等特殊符号,但这些字符表示数据,不表示元字符。直接发给服务器端会导致接收方无法判断谁是元字符谁是数据了。因此,将这部分特殊的字符数据也要进行url编码,这样就不会有歧义。
2.中文同样也会被编码,一般先按照字符集的encoding要求将其转换成字节序列,每一个字节对应的十六进制字符串前面加上百分号即可。
unquote方法:url解码
在百度上搜索汉字“中”:网页上呈现出来的url:https://www.baidu.com/s?wd=中;复制粘贴后呈现出的url:https://www.baidu.com/s?wd=%E4%D8%AD
from urllib.parse import urlencode, unquote u = urlencode({'wd': '中'}) # urlencode对中文的编码 print(u) # wd=%E4%B8%AD url = 'https://www.baidu.com/s?{}'.format(u) print(url) # https://www.baidu.com/s?wd=%E4%B8%AD print('中'.encode('utf-8')) # b'\xe4\xb8\xad' utf-8对中文的编码 print(unquote(u)) # wd=中 unquote解码 print(unquote(url)) # https://www.baidu.com/s?wd=中
参与到实际:假设一个场景需求:连接必应搜索引擎,获取一个搜索的URL:http://cn.bing.com/search?q=马哥教育。通过程序代码完成对关键字“马哥教育”的bing搜索,并将返回的结果保存为网页文件。
from urllib.parse import urlencode, unquote base_url = 'http://cn.bing.com/search' # 是http而不是https d = {'q': '马哥教育'} # 注意必应浏览器下是'q' u = urlencode(d) # q=%E9%A9%AC%E5%93%A5%E6%95%99%E8%82%B2;编码 url = '{}?{}'.format(base_url, u) # ?连接url和关键字参数 print(url) # http://cn.bing.com/search?q=%E9%A9%AC%E5%93%A5%E6%95%99%E8%82%B2 print(unquote(url)) # http://cn.bing.com/search?q=马哥教育;解码可以比对看一下是否有错误 from urllib.request import urlopen, Request # 伪装成浏览器 ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36' req = Request(url, headers={'User-agent': ua}) # 构建http请求对象 res = urlopen(req) # 传入请求对象,建立http连接,获取数据 with res: with open('test_python.html', 'wb') as f: f.write(res.read()) # 将返回的网页内容写入新文件中 print('success')
上述需求讲的是测试get方法的应用,我们可以用http://httpbin.org/这个测试网站,来测试自己post出去的数据是否能正确返回。
from urllib.request import Request, urlopen from urllib.parse import urlencode import simplejson request = Request('http://httpbin.org/post') # post request.add_header('User-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36') data = urlencode({'name': '张三!@#$%^&*(,.)', 'age': '6'}) # post的dataurl编码,放在body里。不做url编码有风险 print(data) print(type(data)) d = data.encode() # POST方法传入的需为json格式的Form表单数据 res = urlopen(request, data=d) with res: text = res.read() print(text) # 包含args,data,files,form,headers等字段的信息;form字段里是post出去的data数据 d = simplejson.loads(text) # bytes转dict print(d)
到这里,python爬虫的‘伪装成浏览器的请求对象->建立http连接->发起post或get请求->查看响应内容 ’理解和简单应用算是成功告一段落啦~
接下来在下一篇里我们就可以开始简单的豆瓣网站爬虫啦~