Python 中可以进行网页解析的库有很多,常见的有 BeautifulSoup 和 lxml 等。在网上玩爬虫的文章通常都是介绍 BeautifulSoup 这个库,我平常也是常用这个库,最近用 Xpath 用得比较多,使用 BeautifulSoup 就不大习惯,很久之前就知道 Reitz 大神出了一个叫 Requests-HTML 的库,一直没有兴趣看,这回可算歹着机会用一下了。
使用pip install requests-html安装,上手和 Reitz 的其他库一样,轻松简单:
from requests_html import HTMLSession
session = HTMLSession()
r = session.get('https://www.python.org/jobs/')
这个库是在 requests 库上实现的,r 得到的结果是 Response 对象下面的一个子类,多个一个html 的属性。所以 requests 库的响应对象可以进行什么操作,这个 r 也都可以。如果需要解析网页,直接获取响应对象的 html 属性:
r.html
不得不膜拜 Reitz 大神太会组装技术了。实际上 HTMLSession 是继承自 requests.Session 这个核心类,然后将 requests.Session 类里的 requests 方法改写,返回自己的一个 HTMLResponse 对象,这个类又是继承自 requests.Response,只是多加了一个_from_response 的方法来构造实例:
class HTMLSession(requests.Session):
# 重写 request 方法,返回 HTMLResponse 构造
def request(self, *args, **kwargs) -> HTMLResponse:
r = super(HTMLSession, self).request(*args, **kwargs)
return HTMLResponse._from_response(r, self)
class HTMLResponse(requests.Response):
# 构造器
@classmethod
def _from_response(cls, response, session: Union['HTMLSession', 'AsyncHTMLSession']):
html_r = cls(session=session)
html_r.__dict__.update(response.__dict__)
return html_r
之后在 HTMLResponse 里定义属性方法 html,就可以通过 html 属性访问了,实现也就是组装 PyQuery 来干。核心的解析类也大多是使用 PyQuery 和 lxml 来做解析,简化了名称,挺讨巧的。
元素定位可以选择两种方式:
# css 获取有多少个职位
jobs = r.html.find("h1.call-to-action")
# xpath 获取
jobs = r.html.xpath("//h1[@class='call-to-action']")
方法名非常简单,符合 Python 优雅的风格,这里不妨对这两种方式简单的说明:
定位到元素以后势必要获取元素里面的内容和属性相关数据,获取文本:
jobs.text
jobs.full_text
获取元素的属性:
attrs = jobs.attrs
value = attrs.get("key")
还可以通过模式来匹配对应的内容:
## 找某些内容匹配
r.html.search("Python {}")
r.html.search_all()
这个功能看起来比较鸡肋,可以深入研究优化一下,说不定能在 github 上混个提交。
除了一些基础操作,这个库还提供了一些人性化的操作。比如一键获取网页的所有超链接,这对于整站爬虫应该是个福音,URL 管理比较方便:
r.html.absolute_links
r.html.links
内容页面通常都是分页的,一次抓取不了太多,这个库可以获取分页信息:
print(r.html)
# 比较一下
for url in r.html:
print(url)
结果如下:
# print(r.html)
<HTML url='https://www.python.org/jobs/'>
# for
<HTML url='https://www.python.org/jobs/'>
<HTML url='https://www.python.org/jobs/?page=2'>
<HTML url='https://www.python.org/jobs/?page=3'>
<HTML url='https://www.python.org/jobs/?page=4'>
<HTML url='https://www.python.org/jobs/?page=5'>
通过迭代器实现了智能发现分页,这个迭代器里面会用一个叫_next 的方法,贴一段源码感受下:
def get_next():
candidates = self.find('a', containing=next_symbol)
for candidate in candidates:
if candidate.attrs.get('href'):
# Support 'next' rel (e.g. reddit).
if 'next' in candidate.attrs.get('rel', []):
return candidate.attrs['href']
通过查找 a 标签里面是否含有指定的文本来判断是不是有下一页,通常我们的下一页都会通过下一页 或者加载更多 来引导,他就是利用这个标志来进行判断。默认的以列表形式存在全局:['next','more','older']。我个人认为这种方式非常不灵活,几乎没有扩展性。感兴趣的可以往 github 上提交代码优化。
也许是考虑到了现在 js 的一些异步加载,这个库支持 js 运行时,官方说明如下:
Reloads the response in Chromium, and replaces HTML content
with an updated version, with JavaScript executed.
使用非常简单,直接调用以下方法:
r.html.render()
第一次使用的时候会下载 Chromium,不过国内你懂的,自己想办法去下吧,就不要等它自己下载了。render 函数可以使用 js 脚本来操作页面,滚动操作单独做了参数。这对于上拉加载等新式页面是非常友好的。
Reitz 大神设计出来的东西还是一如既往的简单好用,自己不多做,大多用别人的东西组装,简化 api。真是够人性。不过有的地方还是优化空间,希望有兴趣和精力的童鞋去 github 上关注一下这个项目。
多朋友都听说过Python的大名,而Python也拥有众多的爬虫框架,其中最简单的莫过于requests-html了。它和著名的网络请求库requests是同一个作者,着重于XML数据提取,可以说是最简单的爬虫框架了。
安装这个类库非常简单,直接通过pip就可以安装了。
pip install requests-html
requests-html用起来也十分简单,下面是一个简单例子。照例说明一下,第一段引入了HTMLSession用于创建连接,获取网页数据。第二段创建连接,获取了我的简书用户页面。第三段用xpath语法获取了网页上的用户名,最后打印出来。
from requests_html import HTMLSession
session = HTMLSession()
response = session.get(
'https://www.jianshu.com/u/7753478e1554')
username = response.html.xpath(
'//a[@class="name"]/text()', first=True)
print(username)
看起来是不是很简单?没错,确实很简单,接下来还有一些更加有趣的功能。
编写爬虫之前还要做一件事情,就是分析网页的结构。这个工作其实也很简单,打开你要访问的网页,按F12打开开发人员工具,可以看到最左边有这么一个按钮。点击这个按钮,然后点击网页上你想要查看的网页元素,然后你就可以发现这个元素对应的相关源代码已经为你定位完毕了。
定位按钮
通过这个功能,我们就可以轻松的分析网页,然后通过它的结构来编写爬虫了。
上面的response.html即是网页的根节点HTML节点,在节点对象上可以调用一些方法来检索数据。最常用的方法是find方法,它通过CSS选择器来定位数据。对于上面的例子,可以用find方法改写第三段。
因为所有查找方法返回的结果都是列表,所以如果你确定只需要查找一个,就将first参数设为真来只返回第一个结果。find方法返回的仍然是一个节点,如果只需要节点的内容,调用其text属性即可。
用户名对应的HTML结构如图所示。
代码如下。
username = response.html.find('a.name', first=True).text
除了find方法之外,还可以使用xpath方法用xpath语法来查找节点,正如第一个例子那样。我个人比较喜欢xpath语法,CSS选择器虽然更加流行一些,但是写出来的效果有点怪,不如xpath工整。
同样是这个页面,看看如何获取我的简书的个人简介。网页代码如图所示。
代码如下。
description = response.html.xpath(
'//div[@class="description"]/div[@class="js-intro"]/text()', first=True)
CSS选择器和XPATH语法都不是本篇的主要内容,如果你这方面不太熟悉,最好去看一下相关的教程。当然如果大家有什么疑问的话,也可以提出来。假如大家想看的话,我也可以专门写一篇文章介绍一下这些语法知识。
有些网页利用了前后端分离技术开发的,需要浏览器渲染才能完整显示。如果用爬虫去看的话,只能显示一部分内容。这时候就需要浏览器渲染页面,才能获取完整的页面。用requests-html的话,这个过程非常简单。
首先先来看看一个需要渲染网页的例子。下面的代码访问了我的简书用户页面,然后尝试获取我的所有文章。但是如果你运行这个例子的话,就会发现只能获取前几项。因为简书的页面正是一个典型的需要浏览器渲染的页面,爬虫获取到的网页是不完整的。
from requests_html import HTMLSession
session = HTMLSession()
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.119 Safari/537.36'
}
url = 'https://www.jianshu.com/u/7753478e1554'
r = session.get(url, headers=headers)
for a in r.html.xpath('//ul[@class="note-list"]/li/div[@class="content"]/a[@class="title"]'):
title = a.text
link = f'https://www.jianshu.com{a.attrs["href"]}'
print(f'《{title}》,{link}')
那么如何渲染网页来获取完整的结果呢?其实非常简单,在查询HTML节点之前,调用render函数即可。
render函数来使用浏览器渲染
原理也非常简单,第一次调用render的时候,requests-html会在本地下载一个chromium浏览器,用它来渲染网页。如此一来,我们就可以获取到渲染之后的页面了。
但是对于简书这个例子来说还是有些问题,因为如果你在浏览器里打开这个网页的话,会发现一些文章在浏览器下滑页面的时候才开始渲染。不过聪慧的作者早就考虑到这种情况了,render函数支持下滑的参数,设定之后,就会模拟浏览器下滑操作,从而解决了这个问题。
r.html.render(scrolldown=50, sleep=0.2)
不论上面的r.html还是find/xpath函数返回的结果,它们都是节点对象。除了上面介绍的几个提取数据的方法以外,节点对象还有以下一些属性,在我们提取数据的时候也有很大作用。
相较于专业的爬虫框架scrapy,或者仅用于解析XML的解析库BeautifulSoup。requests-html可以是说恰到好处,它没有前者那么难学,也不像后者还需要搭配HTTP请求库才能使用。如果你手头需要临时抓取几个网页,那么requests-html就是你最好的选择。
urllib库参考:Python 爬虫之urllib库
Requests是用python语言基于urllib编写的,该模块主要用来发 送 HTTP 请求,requests 模块比 urllib 模块更简洁,是学习 python 爬虫的较好的http请求模块。
不是 python 的内置库,如果没有安装,可以安装先。
pip install requests
requests里提供个各种请求方式,每次调用 requests 请求之后,会返回一个 response 对象,该对象包含了具体的响应信息。响应信息如下:
1、基本Get 请求
import requests
x = requests.get('http://www.baidu.com')
print(x.status_code)
print(x.reason)
print(x.apparent_encoding)
print(x.text)
执行结果:
200
OK
utf-8
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8>.....
请求 json 数据文件,返回 json 内容:
通过params参数传递一个字典内容,从而直接构造url
import requests
params = {
'name': 'zhangsan',
'age': 100
}
x = requests.get('http://httpbin.org/get',params=params)
print(x.url)
print(x.json())
执行结果:
http://httpbin.org/get?name=zhangsan&age=100
{'args': {'age': '100', 'name': 'zhangsan'}, 'headers': {'...
抓取二进制数据
在上面的例子中,我们抓取的是网站的一个页面,实际上它返回的是一个 HTML 文档。如果想抓取图片、音频、视频等文件,需要用到content,这样获取的数据是二进制数据。
如抓取百度logo图片:
import requests
x = requests.get('https://www.baidu.com/img/flexible/logo/pc/result.png')
print(x.content)
执行结果:
b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\xca\x00\
得到的是一串二进制的乱码,如果想得到图片,直接将其保存到本地即可。
import requests
x = requests.get('https://www.baidu.com/img/flexible/logo/pc/result.png')
with open('baidulogo.png','wb') as f:
f.write(x.content)
执行后,在本地目录,可以看到图片已经下载
添加headers
有些网页,必须需要headers才能访问。这时只需要将参赛传到 headers参数中即可,如下所示:
import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'
}
x = requests.get('https://baidu.com/', headers=headers)
print(x.text)
2、基本POST请求
在爬虫中,另外一个比较常见的请求方式就是 POST 请求,跟GET用法差不多。
post() 方法可以发送 POST 请求到指定 url,一般格式如下:
requests.post(url, data={key: value}, json={key: value}, args)
import requests
data = {'name': 'zhangsan', 'age': '100'}
x = requests.post("http://httpbin.org/post", data=data)
print(x.text)
执行结果:
{
"args": {},
"data": "",
"files": {},
"form": {
"age": "100",
"name": "zhangsan"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "21",
....
3、其他请求
其他请求可以自行测试,差不多用法。
r = requests.put('https://httpbin.org/put', data = {'key':'value'})
r = requests.delete('https://httpbin.org/delete')
r = requests.head('https://httpbin.org/get')
r = requests.options('https://httpbin.org/get')
requests 方法如下表:
import requests
# 发送请求
x = requests.request('get', 'https://httpbin.org/')
# 返回网页内容
print(x.status_code)
执行结果:
200
设置请求头:
# 导入 requests 包
import requests
kw = {'s':'python 教程'}
# 设置请求头
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}
# params 接收一个字典或者字符串的查询参数,字典类型自动转换为url编码,不需要urlencode()
response = requests.get("https://httpbin.org", params = kw, headers = headers)
# 查看响应状态码
print (response.status_code)
# 查看响应头部字符编码
print (response.encoding)
# 查看完整url地址
print (response.url)
# 查看响应内容,response.text 返回的是Unicode格式的数据
print(response.text)
执行结果:
200
utf-8
https://httpbin.org/?s=python+%E6%95%99%E7%A8%8B
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>httpbin.org</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700"
rel="stylesheet">
....
1、文件上传
实现方法和其他参数类似,也是构造一个字典然后通过files参数传递。
import requests
files= {"files":open("git.jpeg","rb")}
response = requests.post("http://httpbin.org/post",files=files)
print(response.text)
2、获取cookie
import requests
response = requests.get("http://www.baidu.com")
print(response.cookies)
for key,value in response.cookies.items():
print(key+"="+value)
3、会话维持
cookie的一个作用就是可以用于模拟登陆,做会话维持。
import requests
s = requests.Session()
s.get("http://httpbin.org/cookies/set/number/123456")
response = s.get("http://httpbin.org/cookies")
print(response.text)
4、证书验证
现在的很多网站都是https的方式访问,所以这个时候就涉及到证书的问题,有时遇到:requests.exceptions.SSLError: HTTPSConnectionPool 错误。
import requests
from requests.packages import urllib3
urllib3.disable_warnings()
response = requests.get("https://httpbin.org",verify=False)
print(response.status_code)
当然,我们也可以指定一个本地证书用作客户端证书,这可以是单个文件(包含密钥和证书)或一个包含两个文件路径的元组:
import requests
response = requests.get('https://httpbin.org/', cert=('/path/server.crt', '/path/server.key'))
print(response.status_code)
上面的代码是演示实例,我们需要有 crt 和 key 文件,并且指定它们的路径。另外注意,本地私有证书的 key 必须是解密状态,加密状态的 key是不支持的。
5、代理设置
import requests
from requests.auth import HTTPBasicAuth
response = requests.get("http://192.168.152.100:9001/",auth=HTTPBasicAuth("user","123"))
print(response.status_code)
还有一种方式
import requests
response = requests.get("http://192.168.152.100:9001/",auth=("user","123"))
print(response.status_code)
6、超时设置
在某些网络情况不好的情况下,服务器可能很久才会响应甚至无法响应,此时就需要设置超时来避免无限的等待。在 requests 中,我们可以通过 timeout 参数来设置超时时间,这个时间是发出请求到服务器返回的整个过程所用的时间,即连接和读取两个过程,单位为秒。当然,也可以分别设置连接和读取各自的超时时间:
import requests
r = requests.get('https://httpbin.org/get', timeout=1)
print(r.status_code)
r = requests.get('https://httpbin.org/get', timeout=(5, 30))
当然,如果不设置 timeout 或者将其设置为 None ,则表示不会进行超时处理,即会永久等待下去。
*请认真填写需求信息,我们会在24小时内与您取得联系。