本文档对平常学习中用 python 作数据爬取时所遇到的一些问题作简要记录,以便往后查阅,部分问题可能由于认识不到位会存在一些误解,敬请告知,万分感谢,共同进步。javascript
该小节主要针对于整站爬取的状况。
爬取整站以前,确定是要先对一个网站的规模进行估计。这是可使用google搜索查看大概有多少个网址,这里使用到google搜索的一个小技巧。html
site:url地址
有的时候可查看网站的sitemap.xml,但它有时候会过时或者滞后,不是很准确。html5
要爬的网站使用的技术会影响到咱们所写的代码中的处理手段。
推荐使用builtwith这个第三方包,可使用pip来安装。
简单使用以下:java
In [1]: import builtwith In [2]: builtwith.parse('http://example.webscraping.com/') Out[2]: {u'javascript-frameworks': [u'jQuery', u'Modernizr', u'jQuery UI'], u'programming-languages': [u'Python'], u'web-frameworks': [u'Web2py', u'Twitter Bootstrap'], u'web-servers': [u'Nginx']}
UA,即 User-Agent,是Http协议的一部分,属于头域的组成部分,发送 http 请求时,请求头中会有 User-Agent字段。服务器经过该字段来分辨发送请求的浏览器类型、版本、内核以及操做系统信息等。
在浏览器 console 可用以下命令来得到该浏览器的 UA 标识python
navigator.userAgent
部分网站不想被爬虫爬取就会检查 http 请求头的该字段内容, 因此在用爬虫作抓取时,一般要在请求头加上该字段,以把本身假装成浏览器。有时候经过手机浏览器访问获得的页面会更加简洁,更容易抓取,因此假装成手机浏览器也是一种好方法。
网上有不少整理的不一样浏览器的 UA ,好比各类浏览器UserAgent一览表。
浏览器野史 UserAgent列传(上)和 浏览器野史 UserAgent列传(下),这两篇文章细说了 UA 的前因后果,去感觉下当时波澜壮阔的“浏览器之战”。git
部分服务器会检查 http 请求头的 Referer 字段来判断你是不是从指定页面跳转而来的,以达到防盗链的做用。所以在假装请求头部的时候,该字段也是不容忽视的。github
咱们发现,URL 中有时候存在中文,这是就须要对 url 进行编码。
能够先将中文转换成 utf-8 编码,而后使用 urllib2.quote 方法对参数进行 url 编码后传递。web
import urllib param = u'你好' param = param.encode('utf-8') param = urllib.quote(param)
对于 url 来讲,之因此要进行编码,是由于 url 中有些字符会引发歧义。
同理使用 unquote 能够解码。json
常常会遇到这样状况:将网页拖到底部会自动往下加载新的数据,或者有加载更多这样的按钮,这里说的就是 AJAX 了。api
AJAX 是 Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)的缩写。它经过使用原有的 web 标准组件,实现了在不从新加载整个页面的状况下,与服务器进行数据交互。例如在新浪微博中,你能够展开一条微博的评论,而不须要从新加载,或者打开一个新的页面。可是这些内容并非一开始就在页面中的(这样页面就太大了),而是在你点击的时候被加载进来的。这就致使了你抓取这个页面的时候,并不能得到这些评论信息(由于你没有『展开』)。AJAX 通常是经过 XMLHttpRequest 对象接口发送请求的,XMLHttpRequest 通常被缩写为 XHR。
这些 js 的名字一般看起来与其余的不太同样。
拿澎湃网举个栗子,这才是发送请求得到文章的真正地址,
AJAX 的一种常见用法是使用 AJAX 加载 JSON 数据,而后在浏览器端渲染。
这种状况很好处理,由于 python 自带的处理 json 的库,举个栗子:
豌豆荚安卓游戏排行榜,每次点击更多会加载新的数据。在审查元素里,能够看到每次点击查看更多,都会返回一个包含应用数据详细信息的数据。
查看请求 json 数据的地址
http://apps.wandoujia.com/api...
max 参数的值表示这一次请求返回多少个 app 信息, start 参数的值表示从第几个 app 开始,start 从 0 开始。
另外,在构建请求头时,应该加上这个参数 'X-Requested-With': 'XMLHttpRequest',当使用 XHR 发送 AJAX 请求时 Header 会带上这个字段,常被用于判断是否是 AJAX 请求。
Selenium 是一个强大的网络数据采集工具,最初是为网站自动测试而开发的。它可让浏览器自动加载页面,获取所须要的数据,甚至页面截屏,或者判断网站上某些动做事都发生。
对应的 python 库,能够用 pip 安装。
PhantomJS 是一个 headless browser,它会把网站加载到内存并执行页面上的 JavaScript,可是不会向用户展现网页的图形界面。它不是python库,须要单独下载(喏,你要的官网)。
写一个简单的栗子:
from selenium import webdriver from selenium.webdriver.common.keys import Keys driver = webdriver.PhantomJS(executable_path='<Path to PhantomJs>') driver.get("http://www.python.org") assert "Python" in driver.title elem = driver.find_element_by_name("q") elem.clear() elem.send_keys("pycon") elem.send_keys(Keys.RETURN) assert "No results found." not in driver.page_source driver.close()
Path to PhantomJs 处 须要指定位置,若是该路径已经加入到了环境变量中,那么能够不加这个参数。
这里只是个简单的介绍,后续会对 Selenium 的 python 版 API 的使用作单独介绍。
部分网站对 ip 进行了限制,致使咱们没法爬到想要的数据,这个时候能够用代理来作。
使用 requests 这个第三方库,能够轻松地设置代理。
再举个栗子:
import requests proxies = { 'http': 'http://10.10.1.10:3128', 'https': 'http://10.10.1.10:1080', } requests.get('http://example.org', proxies=proxies)
虽然自带的 urllib 和 urllib2 库能够知足需求,可是不推荐使用。为何?由于它们的操做太繁琐,尤为在处理一些复杂状况时,这不符合 python 的设计哲学,因此放手抛弃它们吧。
推荐使用 requests这个第三方库,正如它标榜的那样--Requests: HTTP for Humans,同时也支持 py3。
使用 requests 库发送请求是如此的优雅,
import requests r = requests.get('https://api.github.com/events') print r.text
具体使用方法能够看官方 API。
既然获取到了请求页面的源码,那么接下来要作的就是解析工做,通常来讲,有下面三个库用得是最多的:lxml库, bs4库,以及正则。
lxml 解析速度要比 bs4 快,听说快好几倍,正则是个终结技,只是写起来有点麻烦。另外, bs4 不支持 xpath,而lxml 支持,总之,视本身的状况选择了。
前几天还接触过一个库,名字叫 pyquery,它是 jQuery 的 python 实现,能够用于解析 html 网页内容,熟悉 jQuery 语法童鞋的福音。
前几天遇到一个问题,问题是这样的,html 页面的数据通过Beautiful Soup库的解析后,部分html源码丢失,找不到想要的数据了,问题代码以下:
#! /usr/bin/env python # -*- coding:utf-8 -*- import requests from bs4 import BeautifulSoup url = 'http://product.pconline.com.cn/mobile/' res = requests.get(url) html = res.text # print html soup = BeautifulSoup(html, 'lxml') site = soup.findAll('img', class_='pic') print site
输出结果为空,没有想要的数据。查看官方文档,bs 库支持的解析库有 lxml, html5lib 和 html.parser。用的最多的是 lxml,由于它的解析速度快,而且容错能力强,默认也是使用该解析器。
出现解析后源码丢失的可能缘由有 2 个:
这里换一个解析器,换成 html.parser 就能够了。
将编码设置为 utf-8
import sys reload(sys) sys.setdefaultencoding('utf-8')
总之,py2 中的编码问题很烦人,只要解码与编码不一致就会出现乱码。对 unicode 能够 编码,其余编码 decode 成 unicode。
要注意'hello' 和 u'hello' 的区别。