用python写网络爬虫

本文介绍了Python网络爬虫的基本概念、URI和URL的区别、urllib和urllib2的使用,包括POST和GET方法、设置headers、代理和Timeout、以及Cookie的处理。详细讲解了如何利用Python进行网络数据抓取,提供了相关实例和代码示例。
摘要由CSDN通过智能技术生成

Python是一门十分强大的语言,要真正了解它的魅力必须通过一些实际运用·。这次通过写一个网络爬虫,我们来体验一下Python在应用中的优势。

关于爬虫

定义:爬虫是一段自动抓取互联网信息的程序。

网络蜘蛛(Web spider)也叫网络爬虫(Web crawler)[1],蚂蚁(ant),自动检索工具(automatic indexer),或者(在FOAF软件概念中)网络疾走(WEB scutter),是一种“自动化浏览网络”的程序,或者说是一种网络机器人。它们被广泛用于互联网搜索引擎或其他类似网站,以获取或更新这些网站的内容和检索方式。它们可以自动采集所有其能够访问到的页面内容,以供搜索引擎做进一步处理(分检整理下载的页面),而使得用户能更快的检索到他们需要的信息。(摘自维基百科)

爬虫实际上是一种自动访问互联网的程序,它从一个给定的URL出发,找到所有相关的超链接,生成一张待访问列表,也叫“爬行疆域”(crawl frontier).然后通过一定机制下载并储存所需要爬去的内容。

关于URI和URL

简单的来讲,URL就是在浏览器端输入的 http://www.baidu.com 这个字符串。

在理解URL之前,首先要理解URI的概念。

什么是URI?

Web上每种可用的资源,如 HTML文档、图像、视频片段、程序等都由一个通用资源标志符(Universal Resource Identifier, URI)进行定位。

URI通常由三部分组成:

①访问资源的命名机制;

②存放资源的主机名;

③资源自身 的名称,由路径表示。

如下面的URI:
http://www.why.com.cn/myhtml/html1223/

我们可以这样解释它:

①这是一个可以通过HTTP协议访问的资源,

②位于主机 www.webmonkey.com.cn上,

③通过路径“/html/html40”访问。

四、URL的理解和举例

URL是URI的一个子集。它是Uniform Resource Locator的缩写,译为“统一资源定位 符”。

通俗地说,URL是Internet上描述信息资源的字符串,主要用在各种WWW客户程序和服务器程序上。

采用URL可以用一种统一的格式来描述各种信息资源,包括文件、服务器的地址和目录等。

URL的一般格式为(带方括号[]的为可选项):

protocol :// hostname[:port] / path / [;parameters][?query]#fragment

URL的格式由三部分组成:

①第一部分是协议(或称为服务方式)。

②第二部分是存有该资源的主机IP地址(有时也包括端口号)。

③第三部分是主机资源的具体地址,如目录和文件名等。

第一部分和第二部分用“://”符号隔开,

第二部分和第三部分用“/”符号隔开。

第一部分和第二部分是不可缺少的,第三部分有时可以省略。

五、URL和URI简单比较

URI属于URL更低层次的抽象,一种字符串文本标准。

换句话说,URI属于父类,而URL属于URI的子类。URL是URI的一个子集。

URI的定义是:统一资源标识符;

URL的定义是:统一资源定位符。

二者的区别在于,URI表示请求服务器的路径,定义这么一个资源。

而URL同时说明要如何访问这个资源(http://)。

举例:
我们一起来看下面这个虚构的例子。这是一个URI:

http://bitpoetry.io/posts/hello.html#intro

我们开始分析 http://是定义如何访问资源的方式。另外 bitpoetry.io/posts/hello.html是资源存放的位置,那么,在这个例子中, #intro是资源。

URL是URI的一个子集,告诉我们访问网络位置的方式。在我们的例子中,URL应该如下所示:

http://bitpoetry.io/posts/hello.html

URN是URI的子集,包括名字(给定的命名空间内),但是不包括访问方式,如下所示:

bitpoetry.io/posts/hello.html#intro

URI可以被分为URL、URN或两者的组合。更简单地说,URI就是表示如何寻找一个人这个问题,而URL和·URN就是通过地址或者通过公司名来找一个人(可能有重叠),这是个人的理解。

参考: 维基百科—网络蜘蛛: https://zh.wikipedia.org/wiki/%E7%B6%B2%E8%B7%AF%E8%9C%98%E8%9B%9B
Python爬虫学习:http://blog.csdn.net/pleasecallmewhy/article/details/8922826

urllib和urllib2

urllib2的urlopen方法使用:urlopen(url, data, timeout)

mport urllib2

response = urllib2.urlopen("http://www.baidu.com")
print response.read()

第一个参数url即为URL,第二个参数data是访问URL时要传送的数据,第三个timeout是设置超时时间。

第二三个参数是可以不传送的,data默认为空None,timeout默认为 socket._GLOBAL_DEFAULT_TIMEOUT

第一个参数URL是必须要传送的,在这个例子里面我们传送了百度的URL,执行urlopen方法之后,返回一个response对象,返回信息便保存在这里面。response对象有read方法,调用后可以显示抓取的内容

urlopen参数可以传入一个request请求,它其实就是一个Request类的实例,构造时需要传入Url,Data等等的内容。比如上面的两行代码,我们可以这么改写

import urllib2

request = urllib2.Request("http://www.baidu.com")
response = urllib2.urlopen(request)
print response.read()

POST和GET

区别:GET方式是直接以链接形式访问,链接中包含了所有的参数,当然如果包含了密码的话是一种不安全的选择,不过你可以直观地看到自己提交了什么内容。POST则不会在网址上显示所有的参数,因此也不会那么直观。
例如:

import urllib
import urllib2

values = {"username":"XXX","password":"XXX"}
data = urllib.urlencode(values) 
url = "https://passport.csdn.net/account/login?from=http://my.csdn.net/my/mycsdn"
request = urllib2.Request(url,data)
response = urllib2.urlopen(request)
print response.read()

以上是一种POST方法,传入URL和data构造request,返回POST之后的响应内容。
再如:

```
import urllib
import urllib2

values = {}
values['username'] = 'xxx'
values['password'] = 'xxx'
data = urllib.urlencode(values)
url = "https://passport.csdn.net/account/login"
get_url = url + "?" + data
request = urllib2.Request(get_url)
response = urllib2.urlopen(request)
print response.read()

设置headers

当我们访问一个网站时,需要一些必要信息来标明一些重要的需求,这个部分叫做消息头。
详细的字段列表可参考:https://zh.wikipedia.org/wiki/HTTP%E5%A4%B4%E5%AD%97%E6%AE%B5%E5%88%97%E8%A1%A8
这里介绍一下http协议相关内容:
http:hypertext transport protocol的缩写,基于TCP/IP协议来传输数据(HTML,图片等),建立于C/S架构之上的无连接,媒体独立,无状态的协议。(无连接指的是一次请求连一次,收到客户端答复后会断开,下次再请求则重新连接;媒体独立指的是只要客户端和服务器知道如何处理数据,则任何类型的数据都可以传;无状态指的是没有记忆力,在需要之前已经访问过的信息时需要再请求一遍)

  • 客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成,下表给出了请求报文的一般格式。
请求方式空格URL空格协议版本
头部字段名回车换行
……..(同上) ………
数据数据数据数据数据

HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。
示例:
客户端请求:

GET /hello.txt HTTP/1.1
User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Host: www.example.com
Accept-Language: en, mi

服务端响应:

HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain

那如何用Python的urllib2设置headers呢?来看个示例:

import urllib  
import urllib2  

url = 'http://www.server.com/login'
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'  
values = {'username' : 'xxx',  'password' : 'XXX' }  
headers = { 'User-Agent' : user_agent }  
data = urllib.urlencode(values)  
request = urllib2.Request(url, data, headers)  
response = urllib2.urlopen(request)  
page = response.read() 

这里设置了用户身份,其他的响应头信息也可以类似地添加,最主要的是我们可以先构造一个消息头字典,然后传入urllib2的Request构造函数中,这样就可以添加消息头了。扩展示例:

我们还有对付”防盗链”的方式,对付防盗链,服务器会识别headers中的referer是不是它自己,如果不是,有的服务器不会响应,所以我们还可以在headers中加入referer

headers = { 'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'  ,
                        'Referer':'http://www.zhihu.com/articles' }  

同上面的方法,在传送请求时把headers传入Request参数里,这样就能应付防盗链了。

设置代理和Timeout

先看一段示例:

import urllib2
enable_proxy = True
proxy_handler = urllib2.ProxyHandler({"http" : 'http://some-proxy.com:8080'})
null_proxy_handler = urllib2.ProxyHandler({})
if enable_proxy:
    opener = urllib2.build_opener(proxy_handler)
else:
    opener = urllib2.build_opener(null_proxy_handler)
urllib2.install_opener(opener)

这里首先定义了一个bool类型的变量,用于标志是否开启代理,然后调用urllib2的ProxyHandler方法,传入一个字典,该字典value为协议名称,key为具体代理域名,如果有不同协议的代理,需设置不同字典值:

proxy_handler = urllib2.ProxyHandler({"http" : 'http://some-proxy.com:8080',"https": "https:xxx:xxx"})

还有就是build_opener方法,这里引用一段解释:

urllib2.install_opener(opener)和urllib2.build_opener([handler, …]) 
  install_opener和build_opener这两个方法通常都是在一起用,也有时候build_opener单独使用来得到OpenerDirector对象。  
  install_opener实例化会得到OpenerDirector 对象用来赋予全局变量opener。如果想用这个opener来调用urlopen,那么就必须实例化得到OpenerDirector;这样就可以简单的调用OpenerDirector.open()来代替urlopen()。
  build_opener实例化也会得到OpenerDirector对象,其中参数handlers可以被BaseHandler或他的子类实例化。子类中可以通过以下实例化:ProxyHandler (如果检测代理设置用), UnknownHandler, HTTPHandler, HTTPDefaultErrorHandler, HTTPRedirectHandler, FTPHandler, FileHandler, HTTPErrorProcessor。 

import urllib2
req = urllib2.Request('http://www.python.org/')
opener=urllib2.build_opener()
urllib2.install_opener(opener)
f = opener.open(req)

  如上使用 urllib2.install_opener()设置 urllib2 的全局 opener。这样后面的使用会很方便,但不能做更细粒度的控制,比如想在程序中使用两个不同的 Proxy 设置等。比较好的做法是不使用 install_opener 去更改全局的设置,而只是直接调用 opener的open 方法代替全局的 urlopen 方法。

关于timeout:

import urllib2
response = urllib2.urlopen('http://www.baidu.com', timeout=10)

这个之前介绍urlopen时就说过有这个参数可选了,这个主要是应对网络较慢的情况,设置多久超时。

使用PUT/DELETE

import urllib2
request = urllib2.Request(uri, data=data)
request.get_method = lambda: 'PUT' # or 'DELETE'
response = urllib2.urlopen(request)

这个不常用,主要是了解这里可以用request的获取方法的方法来使用http的另外两种请求方式。

使用DEBUG LOG

import urllib2
httpHandler = urllib2.HTTPHandler(debuglevel=1)
httpsHandler = urllib2.HTTPSHandler(debuglevel=1)
opener = urllib2.build_opener(httpHandler, httpsHandler)
urllib2.install_opener(opener)
response = urllib2.urlopen('http://www.baidu.com')

通过以上方法把 Debug Log 打开,这样收发包的内容就会在屏幕上打印出来,方便调试。

使用Cookie

Cookie,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)

比如说有些网站需要登录后才能访问某个页面,在登录之前,你想抓取某个页面内容是不允许的。那么我们可以利用Urllib2库保存我们登录的Cookie,然后再抓取其他页面就达到目的了。

opener

当你获取一个URL你使用一个opener(一个urllib2.OpenerDirector的实例)。在前面,我们都是使用的默认的opener,也就是urlopen。它是一个特殊的opener,可以理解成opener的一个特殊实例,传入的参数仅仅是url,data,timeout。

如果我们需要用到Cookie,只用这个opener是不能达到目的的,所以我们需要创建更一般的opener来实现对Cookie的设置。

Cookielib

cookielib模块的主要作用是提供可存储cookie的对象,以便于与urllib2模块配合使用来访问Internet资源。Cookielib模块非常强大,我们可以利用本模块的CookieJar类的对象来捕获cookie并在后续连接请求时重新发送,比如可以实现模拟登录功能。该模块主要的对象有CookieJar、FileCookieJar、MozillaCookieJar、LWPCookieJar。

它们的关系:CookieJar —-派生—->FileCookieJar —-派生—–>MozillaCookieJar和LWPCookieJar

获取Cookie保存到变量

首先,我们先利用CookieJar对象实现获取cookie的功能,存储到变量中,先来感受一下

import urllib2
import cookielib
#声明一个CookieJar对象实例来保存cookie
cookie = cookielib.CookieJar()
#利用urllib2库的HTTPCookieProcessor对象来创建cookie处理器
handler=urllib2.HTTPCookieProcessor(cookie)
#通过handler来构建opener
opener = urllib2.build_opener(handler)
#此处的open方法同urllib2的urlopen方法,也可以传入request
response = opener.open('http://www.baidu.com')
for item in cookie:
    print 'Name = '+item.name
    print 'Value = '+item.value
保存Cookie到文件

在上面的方法中,我们将cookie保存到了cookie这个变量中,如果我们想将cookie保存到文件中该怎么做呢?这时,我们就要用到FileCookieJar这个对象了,在这里我们使用它的子类MozillaCookieJar来实现Cookie的保存

import cookielib
import urllib2

#设置保存cookie的文件,同级目录下的cookie.txt
filename = 'cookie.txt'
#声明一个MozillaCookieJar对象实例来保存cookie,之后写入文件
cookie = cookielib.MozillaCookieJar(filename)
#利用urllib2库的HTTPCookieProcessor对象来创建cookie处理器
handler = urllib2.HTTPCookieProcessor(cookie)
#通过handler来构建opener
opener = urllib2.build_opener(handler)
#创建一个请求,原理同urllib2的urlopen
response = opener.open("http://www.baidu.com")
#保存cookie到文件
cookie.save(ignore_discard=True, ignore_expires=True)

关于最后save方法的两个参数在此说明一下:

官方解释如下:

ignore_discard: save even cookies set to be discarded. 

ignore_expires: save even cookies that have expiredThe file is overwritten if it already exists

由此可见,ignore_discard的意思是即使cookies将被丢弃也将它保存下来,ignore_expires的意思是如果在该文件中cookies已经存在,则覆盖原文件写入,在这里,我们将这两个全部设置为True。运行之后,cookies将被保存到cookie.txt文件中,如图:
cookie

从文件中获取Cookie并访问

那么我们已经做到把Cookie保存到文件中了,如果以后想使用,可以利用下面的方法来读取cookie并访问网站

import urllib2
import cookielib 

#创建MozillaCookieJar实例对象
cookie = cookielib.MozillaCookieJar()
#从文件中读取cookie内容到变量
cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True)
#创建请求的request
req = urllib2.Request("http://www.baidu.com")
#利用urllib2的build_opener方法创建一个opener
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
response = opener.open(req)
print response.read()

这里先用load装载文件,然后用该cookie构造opener,最后处理request,将cookielib和urllib2结合,cookie写入和读取结合,使得一些需要登录才能进行的爬虫迎刃而解。
附上去年爬教务处成绩的代码以供参考(当时还不会python):

#coding=utf-8
import sys
import xlrd
import xlwt
from selenium import webdriver
from bs4 import BeautifulSoup
reload(sys)
sys.setdefaultencoding('utf-8')
phantomjs_path = 'D:\phantomjs-2.1.1-windows\\bin\phantomjs.exe'
driver = webdriver.PhantomJS(phantomjs_path)

URL = 'http://sso.jwc.whut.edu.cn/Certification//login.do'
driver.get(URL)
# 模拟登陆
userName_tag = driver.find_element_by_name('userName')
password_tag = driver.find_element_by_name('password')
# 输入学号和密码
userName_tag.send_keys("xxxxxxxxx")
password_tag.send_keys('xxxxxxxxxx')
driver.find_element_by_id('imageField').submit()
driver.find_element_by_css_selector("a[title=\"成绩、绩点查询\"]").click()
driver.find_element_by_link_text('历史成绩查询').click() # 点击历史成绩查询
driver.implicitly_wait(5) #隐式等待
n = driver.find_element_by_id('lscj_xn')
n.find_element_by_xpath("//option[@value='2015-2016']").click()
driver.find_element_by_xpath("//*[@id='navTab']/div[2]/div[2]/div[1]/form/div/table/tbody/tr/td[6]/input").submit()

print driver.find_element_by_id('undefined').text
data = driver.find_element_by_id('undefined').get_attribute('innerHTML')
soup = BeautifulSoup(data,"html.parser")
tr_tag_l1 = [td.get_text() for td in soup.find_all('td')]
wbk = xlwt.Workbook()
sheet = wbk.add_sheet('sheet 1')
a = 0  # 计算已写个数,用来换行
t = 0  # 记录一共有几行
for b in tr_tag_l1:
    sheet.write(t, a, b)
    a=a+1
    if a==11:
        # 换到下一行,第一列
        a=0
        t=t+1
wbk.save('GPA.xls')
# 读取 excel 文件
wb=xlrd.open_workbook('GPA.xls')
# 找到相应的 sheet 表单
sh = wb.sheet_by_name('sheet 1')
# 对表中的元素进行计算操作
score_point_sum = 0 # 绩点×学分的和
point_sum = 0 # 学分总和
line=1 # 行数
# 在上面统计的总行数 t 之内提取数据计算
while line < t :
    score_point_sum = score_point_sum + float(sh.cell(line,10).value)* float(sh.cell(line,4).value)
    point_sum = point_sum + float(sh.cell(line,4).value)
    line=line+1
# 计算 GPA 并写入 excel 表格
GPA = score_point_sum/point_sum
sheet.write(t+1,0,'GPA')
sheet.write(t+1,1,GPA)
print 'GPA:'
print GPA
wbk.save('GPA.xls')

说实话,没学python,靠着网上的资料完成了这个爬取任务,我都感觉不可思议。这也从侧面说明Python的易用性。附上一个简单版的:

import urllib
import urllib2
import cookielib

filename = 'cookie.txt'
#声明一个MozillaCookieJar对象实例来保存cookie,之后写入文件
cookie = cookielib.MozillaCookieJar(filename)
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
postdata = urllib.urlencode({
            'stuid':'xxx',
            'pwd':'xxx'
        })
#登录教务系统的URL
loginUrl = 'http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bks_login2.login'
#模拟登录,并把cookie保存到变量
result = opener.open(loginUrl,postdata)
#保存cookie到cookie.txt中
cookie.save(ignore_discard=True, ignore_expires=True)
#利用cookie请求访问另一个网址,此网址是成绩查询网址
gradeUrl = 'http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bkscjcx.curscopre'
#请求访问成绩查询网址
result = opener.open(gradeUrl)
print result.read()

正则表达式

此处可参考Python正则表达式指南

由于最近还有一些其他任务,关于正则表达式部分只能先放一放了。正则表达式博大精深,后面我再抽个时间深入学习一下吧,希望今日所写对大家有所帮助。

参考: 网络蜘蛛-维基百科:(https://zh.wikipedia.org/wiki/%E7%B6%B2%E8%B7%AF%E8%9C%98%E8%9B%9B)
爬虫入门:http://cuiqingcai.com/954.html
urllib和urllib2: http://www.cnblogs.com/wly923/archive/2013/05/07/3057122.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值