Python爬虫案例与实战:Python网络爬虫基础-HTTP、HTML、JavaScript、Requests
第一部分爬虫基础篇介绍Python爬虫的基础知识,便于读者熟练掌握相关知识。其中,包括Python语言的一些基本特性、Python解决爬虫问题的一些基本流程(如请求、下载、解析、数据存储)、对应的Python工具包(如Requests用于请求下载,XPath、Python解析JSON、正则表达式、BeautifulSoup等用于解析,Python操作文件、数据库等工具用于数据存储等)。通过本部分的学习,使读者能基本了解一些Python爬虫常用的基本工具。
1.1HTTP、HTML与JavaScript
1.1.1HTTP
HTTP是一个客户端终端(用户)和服务器端(网站)请求和应答的标准。通过使用网页浏览器、网络爬虫或者其他的工具,客户端可以发起一个HTTP请求到服务器上的指定端口(默认端口号为80)。一般称这个客户端为用户代理程序(UserAgent)。应答的服务器上存储着一些资源,如HTML文件和图像。一般称这个应答服务器为源服务器(OriginServer)。在用户代理和源服务器中间可能存在多个“中间层”,如代理服务器、网关或者隧道(Tunnel)。尽管TCP/IP协议是互联网上最流行的应用,HTTP协议中,并没有规定必须使用它或它支持的层。
HTTP假定其下层协议提供可靠的传输。通常,由HTTP客户端发起一个请求,创建一个到服务器指定端口的TCP连接。HTTP服务器则在那个端口监听客户端的请求。一旦收到请求,服务器会向客户端返回一个状态(如“HTTP/1.1200OK”)及返回的内容(如请求的文件、错误消息或者其他信息)。
HTTP的请求方法有很多种,主要包括如下几种。
GET,向指定的资源发出“显示”请求。GET方法应只用于读取数据时,而不应当被用于产生“副作用”的操作中,如在WebApplication中。其中一个原因是GET方法可能会被网络爬虫等随意访问。
POST,向指定资源提交数据,请求服务器进行处理(如提交表单或者上传文件)。数据被包含在请求文本中,该请求可能会创建新的资源或修改现有资源,或二者皆有。
PUT,向指定资源位置上传最新内容。
DELETE,请求服务器删除Request-URI所标识的资源。
TRACE,回显服务器收到的请求,主要用于测试或诊断。
OPTIONS,该方法可使服务器传回该资源所支持的所有HTTP请求方法。用’*'来代替资源名称,向Web服务器发送OPTIONS请求,可以测试服务器功能是否正常运作。
1.1.2HTML
HTML(HyperTextMarkupLanguage,超文本标记语言)是一种用于创建网页的标准标记语言。注意,与HTTP不同的是,HTML是直接与网页相关的,常与CSS、JavaScript一起被众多网站用于设计令人赏心悦目的网页、网页应用程序以及移动应用程序的用户界面。常用的网页浏览器都可以读取HTML文件,并将其渲染成可视化网页。
HTML文档由嵌套的HTML元素构成。它们用HTML标签表示,包含于尖括号中,如
。在一般情况下,一个元素由一对标签表示:“开始标签”
与“结束标签”
。元素如果含有文本内容,就被放置在这些标签之间。在开始与结束标签之间也可以封装另外的标签,包括标签与文本的混合。这些嵌套元素是父元素的子元素。开始标签也可包含标签属性。这些属性有诸如标识文档区段、将样式信息绑定到文档演示和为一些如 等的标签嵌入图像、引用图像来源等作用。一些元素,如换行符,不允许嵌人任何内容,无论是文字还是其他标签。这些元素只需一个单一的空标签(类似于一个开始标签),无须结束标签。浏览器或其他媒介可以从上下文识别出元素的闭合端以及由HTML标准所定义的结构规则。因此,一个HTML元素的一般形式为:<标签属性1="值1"属性2=“值2”>内容</标签>。一个HTML元素的名称即为标签使用的名称。注意,结束标签的名称前面有一个斜杠“/”,空元素不需要也不允许结束标签。如果元素属性未标明,则使用其默认值。
1.1.3JavaScript
现代网页除了HTTP和HTML,还会涉及JavaScript技术。人们看到的浏览器中的页面,其实是在HTML的基础上,经过JavaScript进一步加工和处理后生成的效果。例如,淘宝网的商品评论就是通过JavaScript获取JSON数据,然后“嵌入”到原始HTML中并呈现给用户。这种在页面中使用JavaScript的网页对于20世纪90年代的Web界面而言几乎是天方夜谭,但在今天,以AJAX(AsynchronousJavaScriptAndXML,异步JavaScript与XML)技术为代表的结合JavaScript、CSS、HTML等语言的网页开发技术已经成为了绝对的主流。JavaScript使得网页可以灵活地加载其中的一部分数据。后来,随着这种设计的流行,AJAX这个词语也成为一个“术语”。
JavaScript一般被定义为一种“面向对象、动态类型的解释型语言”,最初由Netscape(网景)公司推出,目的是作为新一代浏览器的脚本语言支持,换句话说,不同于PHP或者ASP.NET,JavaScript不是为“网站服务器”提供的语言,而是为“用户浏览器”提供的语言。为了在网页中使用JavaScript,开发者一般会把JavaScript脚本程序写在HTML的
JavaScript在语法结构上类似于 C++等面向对象的语言,循环语句、条件语句等也都与Python 中的写法有较大的差异,但其弱类型特点更符合Python 开发者的使用习惯。一段简单的JavaScript 脚本程序如下:
function add(a,b) {
var sum = a+ b; console. log(' %d + %d equals to %d',a, b, sum);
}
function mut(a, b) {
var prod = a * b; console. log(' %d *
%d equals to %d',a,b, prod);
}
接着,通过下面的例子来展示JavaScript的基本概念和语法,代码如下:
var a =1;//变量声明与赋值
//变量都用var关键字定义
var myFunction-function(arg1){
//注意这个赋值语句,在 JavaScript 中,函数和变
//量本质上是一样的
arg1 +=1;
return arg1;
}
var myAnotherFunction = function(f,a){
//函数也可以作为另一个函数的参数被传人
return f(a);
}
console.log(myAnotherFunction(myFunction,2))
//条件语句
if(a >0){
a-= 1;
}else if(a ==
0){
a-= 2;
}else{
a 十=2;
//
arr =[1,2,3];
console.log(arr[1]);
//xg myAnimal ={name:"Bob",
species:"Tiger",
gender:"Male",
isAlive:true,isMammal:true,console.log(myAnimal.gender);
//访问对象的属性
其实,人们所说的AJAX 技术,与其说是一种“技术”,不如说是一种“方案”。AJAX 技术改变了过去用户浏览网站时一个请求对应一个页面的模式,允许浏览器通过异步请求来获取数据,从而使得一个页面能够呈现并容纳更多的内容,同时也就意味着更多的功能。只要用户使用的是主流的浏览器,同时允许浏览器执行JavaScript,用户就能够享受网站在网页中的AJAX内容。
以知乎的首页信息流为例,如图1-2所示,与用户的主要交五互方式就是用户通过下拉页面
(可通过鼠标滚轮、拖动滚动条等)查看更多动态,而在一部分动态(对于知乎而言包括被关注用户的点赞和回答等)展示完毕后,就会显示一段加载动画并呈现后续的动态内容。在这个过程中,页面动画其实只是“障眼法”,在这个过程中,正是JavaScript 脚本请求了服务器发送相关数据,并最终加载到页面之中。在这个过程中,页面显然没有进行全部刷新,而是只“新”刷新了一部分,通过这种异步加载的方式完成了对新内容的获取和呈现,这个过程就是典型的AJAX应用。
1.2Requests的使用
1.2.1 Requests简介
Requests库,作为Python最知名的开源模块之一,目前支持 Python 2.6~2.7以及Python 3的所有版本,Requests由 Kenneth Reitz开发,如图1-3所示,其设计和源码也符合Python风格(这里称为Pythonic)。
作为HTTP库,Requests的使命就是完成HTTP请求。对于各种HTTP请求,Requests都能简单漂亮地完成,当然,其中 GET方法是最为常用的:
r = requests.get(URL)
r = requests.put(URL)
r = requests.delete(URL)
r = requests.head(URL)
r = requests.options(URL)
如果想要为URL的查询字符串传递参数(如当你看到了一个URL 中出现了“?xxx= yyy&.aaa=bbb”时),只需要在请求中提供这些参数,就像这样:
comment_json_url =‘https://sclub.jd.com/comment/productPageComments.action’P_data ={
’ callback’: ’ fetchJSON_comment98vv242411’,
’ score’: 0,
’ sort Type’: 1,
’ page’: 0,
’ pageSize’: 10,
’ isthadowSku’: 0,
response -requests. get(comment_json _url, params = p_data)
其中,p_data是一个dict结构。打印出现在的URL,可以看到 URL的编码结果,输出如下:
https://sclub.jd.com/comment/productPageComments,action?page = 0&isShadowSku = 0&sortType =
1&callback = fetchJSON_comment98vv242411&pageSize = 10&score = 0
使用.text来读取响应内容时,Requests会使用 HTTP 头部中的信息来判断编码方式。当然,编码是可以更改的,如下:
print(response.encoding)#会输出"GBK"response.encoding ='UTF-8'
text有时候很容易和content混淆,简单地说,text表达的是编码后(一般就是Unicode编码)的内容,而content是字节形式的内容。
Requests中还有一个内置的JSON解码器,只需调用r.json()即可。在爬虫程序的编写过程中,经常需要更改HTTP请求头。正如之前很多例子那样,想为请求添加HTTP 头部,只要简单地传递一个dict给headers参数就可以。r.status_code是另外一个常用的操作,这是一个状态码对象,可以利用如下方式检测HTTP请求对象。
print(r.status_code == requests.codes.ok)
实际上Requests还提供了更简洁(简洁到不能更简洁,与上面的方法等效)的方法。
print(r.ok)
在这里r.ok是一个布尔值。
如果是一个错误请求(4XX 客户端错误或5XX服务器错误响应),可以通过 Response.raise_for_status()来抛出异常。
1.2.2使用 Requests编写爬虫程序
在各大编程语言中,初学者要学会编写的第一个简单程序一般就是“Hello,World!”,即通过程序在屏幕上输出一行“Hello,World!”这样的文字,在Python中,只需一行代码就可以做到。受到这种命名习惯的影响,把第一个爬虫称为 HelloSpider,代码如下:
import lxml. html, requests
url = ' https://www. Python. org/dev/peps/pep -0020/'
xpath = '//*[@ id= "the-zen-of -Python"J/pre/text()'
res = requests. get(url)
ht = lxml. html. fromstring(res. text)
text = ht. xpath(xpath)
print(' Hello, \n'+ ". join(text))
执行这个脚本,在终端中运行如下命令(也可以在直接IDE 中单击“运行”):
Python HelloSpider.py
很快就能看到输出如下:
Hello,
Beautiful is better than ugly.
......
Namespaces are one honking great idea--let's do more of those!
至此,程序完成了一个网络爬虫程序最普遍的流程:①访问站点;②定位所需的信息;③得到并处理信息。接下来不妨看看每行代码都做了什么:
import lxml.html,requests
这里使用import导入了两个模块,分别是lxml库中的html以及Python 中著名的requests库。1xml用于解析XML和HTML的工具,可以使用 xpath 和 css来定位元素,这里还导人了requests。
url ='https://www.Python.org/dev/peps/pep-0020/'
xpath ='//*[@id ="the-zen-of-Python"]/pre/text()'
上面定义了两个变量,Python不需要声明变量的类型,url和xpath会自动被识别为字符串类型。url是一个网页的链接,可以直接在浏览器中打开,页面中包含了“Python之禅”的文本信息;xpath变量则是一个xpath路径表达式,而lxml库可以使用 xpath来定位元素,当然,定位网页中元素的方法不止xpath 一种,以后会介绍更多的定位方法。
res = requests.get(url)
这里使用了requests中的get方法,对url 发送了一个HTTP GET请求,返回值被赋值给res,于是便得到了一个名为res的 Response对象,接下来就可以从这个Response对象中获取想要的信息。
ht = lxml.html.fromstring(res.text)
lxml.html是lxml下的一个模块,顾名思义,主要负责处理HTML。fromstring 方法传入的参数是res.text,即 Response对象的text(文本)内容。在fromstring函数的doc string 中(文档字符串,即这个函数的说明,可以通过print(‘lxml.html.fromstring.__doc__查看’))说道,这个方法可以“Parse the html,returning a single element/document."即 fromstring 根据这段文本来构建一个lxml中的HtmlElement对象。
text = ht.xpath(xpath)
print('Hello,\n'+'.join(text))
如上两行代码使用 xpath来定位HtmlElement 中的信息,并进行输出。text就是得到的结果,“.join()”是一个字符串方法,用于将序列中的元素以指定的字符连接生成一个新的字符串。因为text是一个list对象,所以使用"这个空字符来连接。如果不进行这个操作而直接输出:
print('Hello,\n'+ text)
程序会报错,出现“TypeError:Can’t convert’list’object to str implicitly”这样的错误。当然,对于list序列而言,还可以通过一段循环来输出其中的内容。
通过刚才这个十分简单的爬虫示例,不难发现,爬虫的核心任务就是访问某个站点(一般为一个或一类URL 地址),然后提取其中的特定信息,之后对数据进行处理(在这个例子中只是简单地输出)。当然,根据具体的应用场景,爬虫可能还需要很多其他的功能,如自动抓取多个页面、处理表单、对数据进行存储或者清洗等。