爬虫是我们快速获取需要的数据的一个非常有效的途径,而爬虫的第一步便是能够请求远方服务器为我们返回所需的网页信息。我们知道,正常情况下在浏览器上我们只要输入正确的统一资源定位器url,即网页地址便可轻松打开我们想要看到页面。同理,在设计python爬虫程序时,我们也可以调用对应的库通过参数设置来连接网络处理http协议。针对静态的网页,常常被使用到的库有urllib, urllib2, requests等等,通过它们可以很方便的请求服务器返回特定地址的网页内容。但是如果遇到JS加载的动态网页,用前面的方法往往收不到我们想要的结果。这时候就可以召唤出强大的自动化测试工具Selenium并喊上它的小伙伴PhantomJS来一起升级打怪了。
(一) urllib, urllib2,直接上例子:
import urllib2 response = urllib2.urlopen("http://www.baidu.com") print response.read()
上面只要给一个url,例如百度,调用urllib2库便可以单枪匹马读到这个网址对应的网页源码,代码特别简洁。实际爬虫中由于考虑到对方的反爬虫机制,网络响应时间或者发送请求需要添加额外的信息 ,我们需要多添加几行代码,目的是让服务器尽量相信收到的请求都是来自正常的访问对象。为了程序逻辑清晰,我们可以为urlopen设计一个request对象作为传入参数,例如:
import urllib import urllib2 #添加url url = 'xxx' request = urllib2.Request(url) #为了模拟浏览器行为,伪装对方识别问题,可以添加Headers属性,例如下面的agent便是设置请求身份: user_agent = 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36' headers = { 'User-Agent' : user_agent} request = urllib2.Request(url, headers) #有时访问某些网站需要提供一些信息,例如用户名和密码,这时可以这样: values = {"username":"yourname","password":"????"} data = urllib.urlencode(values) request = urllib2.Request(url, data, headers) #在遇到网络状况不好时可以设置timeout来设置等待多久超时 response = urllib2.urlopen(request,timeout=18) print response.read()
更多的相关内容可以查看这个urllib和urllib2。
(二)requests,一款简单美观而友好的外部库。
requests所有的功能都可以通过以下7个方法访问。它们全部都会返回一个response对象的实例。
#创建并发送一个request requests.request(method, url, **kwargs) 参数: method -- method for the new Request object. url -- URL for the new Request object. params -- (optional) Dictionary or bytes to be sent in the query string for the Request. data -- (optional) Dictionary, bytes, or file-like object to send in the body of the Request. json -- (optional) json data to send in the body of the Request. headers -- (optional) Dictionary of HTTP Headers to send with the Request. cookies -- (optional) Dict or CookieJar object to send with the Request. files -- (optional) Dictionary of 'name': file-like-objects (or {'name': file-tuple}) for multipart encoding upload. file-tuple can be a 2-tuple ('filename', fileobj), 3-tuple ('filename', fileobj, 'content_type') or a 4-tuple ('filename', fileobj, 'content_type', custom_headers), where 'content-type' is a string defining the content type of the given file and custom_headers a dict-like object containing additional headers to add for the file. auth -- (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth. timeout (float or tuple) -- (optional) How long to wait for the server to send data before giving up, as a float, or a (connect timeout, read timeout) tuple. allow_redirects (bool) -- (optional) Boolean. Set to True if POST/PUT/DELETE redirect following is allowed. proxies -- (optional) Dictionary mapping protocol to the URL of the proxy. verify -- (optional) whether the SSL cert will be verified. A CA_BUNDLE path can also be provided. Defaults to True. stream -- (optional) if False, the response content will be immediately downloaded. cert -- (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. #例如: import requests url='xxxx' headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'} proxies = { 'http' : '127.0.0.1:8118'} response=requests.request('GET',url, timeout=20, proxies=proxies, headers=headers) 返回类型requests.Response 另: #发送一个HEAD request. requests.head(url, **kwargs) #发送一个GET request requests.get(url, params=None, **kwargs) #发送一个POST request requests.post(url, data=None, json=None, **kwargs) #发送一个PUT request requests.put(url, data=None, **kwargs) #发送一个PATCH request requests.patch(url, data=None, **kwargs) #发送一个DELETE request requests.delete(url, **kwargs)更详细的介绍可以参考官网 requests。
(三)Selenium + PhantomJs,高效处理动态网页的完美组合。
我们使用前面的方法可以单纯的获取网页的html代码,如若是遇到要经过JS渲染的网页内容时则会变得非常麻烦。所以我们需要能够像浏览器一样处理要JS渲染的页面的工具,而PhantomJs正是一款基于WebKit的无界面的网页交互工具,其提供的JavaScript API可以实现自动化浏览、截屏等浏览器功能。Selenium是一款自动化测试工具,其支持Firefox,Chrome,Safari等主流的浏览器,借助selenium就可以模拟人类的各种网页操作行为,例如打开浏览器,输入信息,点击,翻页等等。PhantomJS作为一款无界面的浏览器,Selenium会对它感冒吗?答案是非常感冒,因为PhantomJs不仅能完成浏览器的功能,其效率相对来说还更高。例如:
from selenium import webdriver from selenium.webdriver.common.keys import Keys phantomjs_path = '/data/opt/brew/lib/node_modules/phantomjs/lib/phantom/bin/phantomjs' driver = webdriver.PhantomJS(executable_path=phantomjs_path) url = 'xxxx' driver.get(url) #轻松得到到JS渲染页面的源码 page_source = driver.page_source.encode('utf8')有时候我们需要做到页面交互,即可以模拟人在浏览器上点击,输入,鼠标移动等各种行为时,这就需要先做到对页面元素的定位问题。其中WebDriver提供了多种方法来实现元素定位:
#定位一个元素的方法有 find_element_by_id find_element_by_name find_element_by_xpath find_element_by_link_text find_element_by_partial_link_text find_element_by_tag_name find_element_by_class_name find_element_by_css_selector #定位多元素,返回一个list,方法有 find_elements_by_name find_elements_by_xpath find_elements_by_link_text find_elements_by_partial_link_text find_elements_by_tag_name find_elements_by_class_name find_elements_by_css_selector #例如有如下网页源码: <html> <body> <form id="loginForm"> <input name="username" type="text" /> <input name="password" type="password" /> <input name="continue" type="submit" value="Login" /> <input name="continue" type="button" value="Clear" /> </form> </body> <html> #form可以这样来定位 login_form = driver.find_element_by_id('loginForm') #username&password两个元素定位如下 username = driver.find_element_by_name('username') password = driver.find_element_by_name('password') #如果使用xpath来定位username,以下方法都ok username = driver.find_element_by_xpath("//form[input/@name='username']") username = driver.find_element_by_xpath("//form[@id='loginForm']/input[1]") username = driver.find_element_by_xpath("//input[@name='username']")更多内容可以参考 selenium_python, PhantomJS。
这篇文章简短介绍了网页源码内容获取的各种方法,包括静态网页常常使用到的urllib,urllib2,requests和动态网页使用到的selenium,phantomjs组合。在爬虫过程中,我们往往需要提取并保留网页中有用的信息,所以接下来要介绍的是如何针对获得的网页源码提取出有用信息的工作。