Python是一门高级的动态的语言,利用它进行网络数据的抓取是非常方便的。代码的实现非常的精简。
在本次python爬虫中,我们将使用到以下python第三方库:
1.requests(urllib3) 用于发起http请求,相对于python自带的urllib2模块,更加的pythonic
2.redis-py 连接redis数据库,用于保存待抓取的url队列,并实现分布式
3.gevent 实现并发抓取,相对于python的threading性能更好,但是有monkey-patch
4.pybloom 布隆过滤器,实现url的去重功能
5.MySQL-python 存储网页以及提取到的结构化数据
6.Chardet 检测网页编码
7.Parsel 基于lxml实现的网页解析器,用法更简单,scrapy用的就是这个,速度可以媲美lxml
8.Selenium + phantomjs 抓取js网页
以上这些就是目前在爬虫实现中会用到的python库,这对于目前的我来说,是最优的解决方法。当然,如果有更好的方法可以代替上面列出的一些第三方库,我也会及时更新。
言归正传,开始我们的网页抓取旅途吧!
首先,我们探讨一下,我们从客户端发起一个http请求到接受到一个response中间经历了哪些过程?
1.我们需要一个用于请求的url
2.dns解析,将我们请求url中的host提取出来,并进行相应的dns解析,解析成ip地址,其中http请求的端口号默认是80, https请求的默认端口号是443
3.根据解析出来的ip地址跟端口号以及host,进行tcp/ip连接,绑定到特定的主机上
4.发送http请求,优先发送headers,如果拥有body部分,再发送body部分
5.接受http响应
以上步骤中我们忽略代理这个中间件
通过以上五个步骤,我们就可以开始构建我们的第一个请求了。
Requests库的用法非常的简单,简单的两句就可以抓取一个网页:
url = 'http://www.example.com'
r = requests.get(url)
Dns解析,tcp/ip链接,发送http请求我们完全不需要去考虑,requests已经帮我们都实现了。有兴趣的同学,可以参考python内置模块httplib
只要成功请求,就会返回一个response对象,否则就会抛出一个异常,requests的所有异常都继承自requests.exceptions.RequestException.类。
我们可以通过r.status_code来查看网页的返回码,正常网页的返回码是200,如果返回的是一个已经不存在的网页,状态码就是400+, 如果是服务器错误,就是500+。默认情况下是不会返回300+的状态码的,因为requests会自动为我们重定向。如果要禁用重定向,传入allow_redirects=False关键词参数即可。在我们的爬虫进行网络数据抓取的时候,建议大家最好禁用重定向,因为重定向会非常的耗时,比如存在dns解析,tcp/ip链接,然后再发起http请求,之后等待响应。
在http请求的时候,requests会给我们带上一个headers,里面有一些默认的参数值。可以通过response.request.headers进行查看,下面我们以请求百度首页为例,看看headers会默认带上哪些参数。
{‘accept-encoding’: ‘gzip, deflate’, ‘accept’: ‘/’, ‘user-agent’: ‘python-requests/版本号’ }
这是requests默认给我们带上的参数。
’accept-encoding’向服务器表明了我们客户端可以接受的数据编码类型,只要服务器探测到客户端的请求的头部中有这个字段,那么就会发送相应的编码的数据过去。默认情况下我们接受到的response都是经过gzip压缩过的,因为目前来说gzip压缩的比率是最好的,这样可以减少数据的传输,提高性能,减少延迟。
‘accept’向服务器表明了我们客户端可以接受哪种数据类型,/表示我们可以接受任何类型。但事实上并不是这样,大部分时候我们只需要接受html文本即可。
‘User-agent’ 相信大部分写过爬虫的都对这个不会陌生,user-agent用来向服务器表明客户端的身份。有一些反爬虫较为严格的服务器会根据user-agent来决定是够返回响应数据,大部分时候,我们的做法是去获取一大批浏览器的user-agent,这种做法可以给我们提供不少的帮助。
如果返回响应的是文本内容,使用r.text即可,二进制数据则使用r.content。
前面我们使用了requests.get(url)这种方法来请求url,在实际使用中你就会发现,这很容易被禁止掉。所以我们需要做一些设置。
headers = {'user-agent': 'xxxx',
'host': 'xxx',
'accept': 'xxx',
'accept-charset': 'xxx',
'accept-language': 'xxx',
'referer': 'xxxx',}
1.user-agent用户代理,百度谷歌一下随便找
2.host请求url的host, 可使用urlparse模块
3.accept如果只能接受html,就写成text/html,具体参考可去w3school上去看详细内容,也可以看看《http权威指南》
4.accept-charset建议统一为utf-8,python里面的字符编码问题谁用谁知道
5.accept-language如果请求中文页面就统一为zh-cn
6.referer:请求url的来源页面,举个列子,我们在浏览器在通过连接点击下一个页面,就会有referer头部,这对服务器来说是友好的。当然还有一些条件请求头部,比如if-modified-since等,这留到以后讲解。
上面的这些headers实在不了解的话,推荐可以看看《http权威指南》
最后加上requests中的一些参数,我们最后的请求如下:
import requests
headers = {'user-agent': "Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/50.0.2657.3+Safari/537.36",
'host': url_host,
'accept': 'text/html',
'accept-charset': 'utf-8',
'accept-language': 'zh-cn',
'referer': referer_url,}
#如果想要再加上一些其他的请求头部或者完善请求头部,推荐书籍《http权威指南》
requests.get(url=url,
headers=headers,
timeout=5,
allow_redirects=False)
当然requests和urllib3中还有其他非常多的功能,比如连接池,会话链接,安全认证,更换代理,cookie处理等等,这都是我们在写爬虫的时候会用到的,具体参考官方指南