一 介绍Python上有一个非常著名的HTTP库——requests,相信大家都听说过,用过的人都说非常爽!现在requests库的作者又发布了一个新库,叫做requests-html,看名字也能猜出来,这是一个解析HTML的库,具备requests的功能以外,还新增了一些更加强大的功能,用起来比requests更爽!接下来我们来介绍一下它吧。 # 官网解释'''This library intends to make parsing HTML (e.g. scraping the web) as simple and intuitive as possible. 官网告诉我们,它比原来的requests模块更加强大,并且为我们提供了一些新的功能!
二 安装安装requests-html非常简单,一行命令即可做到。需要注意一点就是,requests-html只支持Python 3.6或以上的版本,所以使用老版本的Python的同学需要更新一下Python版本了。 # pip3 install requests-html 三 如何使用requests-html?在我们学爬虫程序的时候用得最多的请求库就是requests与urllib,但问题是这些包只给我们提供了如何去目标站点发送请求,然后获取响应数据,接着再利用bs4或xpath解析库才能提取我们需要的数据。 以往爬虫的请求与解析 而在requests-html里面只需要一步就可以完成而且可以直接进行js渲染!requests的作者Kenneth Reitz 开发的requests-html 爬虫包 是基于现有的框架 PyQuery、Requests、lxml、beautifulsoup4等库进行了二次封装,作者将Requests的简单,便捷,强大又做了一次升级。 requests-html和其他解析HTML库最大的不同点在于HTML解析库一般都是专用的,所以我们需要用另一个HTTP库先把网页下载下来,然后传给那些HTML解析库。而requests-html自带了这个功能,所以在爬取网页等方面非常方便。 1、基本使用from requests_html import HTMLSession 2、获取链接(links与abolute_links)links返回的结果 absolute_links返回的结果
from requests_html import HTMLSession 3、CSS选择器与XPATHrequest-html支持CSS选择器和XPATH两种语法来选取HTML元素。首先先来看看CSS选择器语法,它需要使用HTML的 find 函数来查找元素。 ''' CSS选择器 and XPATH 1.通过css选择器选取一个Element对象 2.获取一个Element对象内的文本内容 3.获取一个Element对象的所有attributes 4.渲染出一个Element对象的HTML内容 5.获取Element对象内的特定子Element对象,返回列表 6.在获取的页面中通过search查找文本 7.支持XPath 8.获取到只包含某些文本的Element对象''' view code 四 支持JavaScript支持JavaScript是我觉得作者更新后最为牛逼的一个地方,但是需要在第一次执行render的时候下载chromeium,然后通过它来执行js代码。 1、render的使用from requests_html import HTMLSession 注意:第一次运行render()方法时,它会将Chromium下载到您的主目录中(例如~/.pyppeteer/)。这种情况只发生一次。 2、 下载Chromeium问题因为是从国外的站点下载几分钟才3%,实在是太慢了。所以我们需要通过国内的镜像去下载!需要做以下几步:
五 自定义User-Agent 有些网站会使用User-Agent来识别客户端类型,有时候需要伪造UA来实现某些操作。如果查看文档的话会发现 from requests_html import HTMLSession# pprint可以把数据打印得更整齐from pprint import pprintimport json get_url = 'http:///get' 六 模拟表单提交(POST)
# 表单登录r = session.post('http:///post', data={'username': 'tank_jam', 'password': 'tank9527'}) pprint(json.loads(r.html.html))''' # 打印结果{'args': {}, 'data': '', 'files': {}, 'form': {'password': 'tank9527', 'username': 'tank_jam'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Content-Length': '35', 'Content-Type': 'application/x-www-form-urlencoded', 'Host': '', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) ' 'AppleWebKit/603.3.8 (KHTML, like Gecko) ' 'Version/10.1.2 Safari/603.3.8'}, 'json': None, 'origin': '112.65.61.109, 112.65.61.109', 'url': 'https:///post'}''' 七 支持异步请求requests-html内部就封装好了aynsc异步请求的功能,可以提高我们的爬虫效率。 from requests_html import AsyncHTMLSessionfrom requests_html import HTMLSessionimport time 1. 开始Python 中可以进行网页解析的库有很多,常见的有 BeautifulSoup 和 lxml 等。在网上玩爬虫的文章通常都是介绍 BeautifulSoup 这个库,我平常也是常用这个库,最近用 Xpath 用得比较多,使用 BeautifulSoup 就不大习惯,很久之前就知道 Reitz 大神出了一个叫 Requests-HTML 的库,一直没有兴趣看,这回可算歹着机会用一下了。 使用 from requests_html import HTMLSession 这个库是在 requests 库上实现的,r 得到的结果是 Response 对象下面的一个子类,多个一个 r.html 2. 原理不得不膜拜 Reitz 大神太会组装技术了。实际上 HTMLSession 是继承自 requests.Session 这个核心类,然后将 requests.Session 类里的 requests 方法改写,返回自己的一个 HTMLResponse 对象,这个类又是继承自 requests.Response,只是多加了一个 class HTMLSession(requests.Session): class HTMLResponse(requests.Response): 之后在 HTMLResponse 里定义属性方法 html,就可以通过 html 属性访问了,实现也就是组装 PyQuery 来干。核心的解析类也大多是使用 PyQuery 和 lxml 来做解析,简化了名称,挺讨巧的。 3. 元素定位元素定位可以选择两种方式: css 选择器
# css 获取有多少个职位 方法名非常简单,符合 Python 优雅的风格,这里不妨对这两种方式简单的说明: 4. CSS 简单规则
5. Xpath简单规则
定位到元素以后势必要获取元素里面的内容和属性相关数据,获取文本: jobs.text 获取元素的属性: attrs = jobs.attrs 还可以通过模式来匹配对应的内容: ## 找某些内容匹配 这个功能看起来比较鸡肋,可以深入研究优化一下,说不定能在 github 上混个提交。 6. 人性化操作除了一些基础操作,这个库还提供了一些人性化的操作。比如一键获取网页的所有超链接,这对于整站爬虫应该是个福音,URL 管理比较方便: r.html.absolute_links 内容页面通常都是分页的,一次抓取不了太多,这个库可以获取分页信息: print(r.html) 结果如下: # print(r.html) 通过迭代器实现了智能发现分页,这个迭代器里面会用一个叫 def get_next(): 通过查找 a 标签里面是否含有指定的文本来判断是不是有下一页,通常我们的下一页都会通过 7. 加载 js也许是考虑到了现在 js 的一些异步加载,这个库支持 js 运行时,官方说明如下:
使用非常简单,直接调用以下方法: r.html.render() 第一次使用的时候会下载 Chromium,不过国内你懂的,自己想办法去下吧,就不要等它自己下载了。render 函数可以使用 js 脚本来操作页面,滚动操作单独做了参数。这对于上拉加载等新式页面是非常友好的。 8. 总结Reitz 大神设计出来的东西还是一如既往的简单好用,自己不多做,大多用别人的东西组装,简化 api。真是够人性。不过有的地方还是优化空间,希望有兴趣和精力的童鞋去 github 上关注一下这个项目。 昨天写了requests库好!最近requests库的作者又发布了一个新库,叫做requests-html,看名字也能猜出来,这是一个解析HTML的库,而且用起来和requests一样爽,下面就来介绍一下它。 一、安装pip install requests-html 二、基本使用获取网页from requests_html import HTMLSession 获取链接
# 获取链接print(r.html.links)print(r.html.absolute_links) 结果如下 {'/article/104353012', '/article/120616112', '/users/32331196/'} {'https://www./imgrank/', 'https://www./article/120669516', 'https://www./article/120682041'} 获取元素request-html支持CSS选择器和XPATH两种语法来选取HTML元素。首先先来看看CSS选择器语法,它需要使用HTML的find函数,该函数有5个参数,作用如下:
例子: # 首页菜单文本print(r.html.find('div#menu', first=True).text)# 首页菜单元素print(r.html.find('div#menu a'))# 段子内容print(list(map(lambda x: x.text, r.html.find('div.content span')))) 结果如下, 热门 24小时 热图 文字 穿越 糗图 新鲜 [<Element 'a' href='/' rel=('nofollow',)>, <Element 'a' href='/hot/'>, <Element 'a' href='/imgrank/'>, <Element 'a' id='highlight' href='/text/'>, <Element 'a' href='/history/'>, <Element 'a' href='/pic/'>, <Element 'a' href='/textnew/'>] ['有一次,几位大城市的朋友来家里玩,我招待他们吃风干羊肉做臊子的饸饹面,这是我们老家最具特色的美食!饭快熟的时候,老婆让我在园子里摘点“芫荽 ”,朋友问我,“芫荽”是什么东东?我给他们翻译解释说:我们本地土话叫“芫荽”,你们城里人讲普通话叫香菜,他们还大笑了一场。\n前天下雨没事儿干,翻看新华字典,突然发现“芫荽”才是香菜的学名,Tm香菜才是土话!而且我们地方方言就这两个字发音还特别标准!', '昨天晚上跟老婆吵架,他抓起我的手机就摔了。我立马摔了他的,结果我的还能用,他的坏了。高潮是人家立刻出门买了个新的!我艹,是不是中计了??', '小姨要去高铁站,我看着大大小小的箱子说:坐公交车要转车,转来转去要一个多小时,太不方便了,不如我开车送你吧。\n小姨迟疑了一下,同意了。\n我准时把小姨送到了高铁站,正好赶上检票。\n小姨高兴地说:自己开车就是方便,不过幸好你妈聪明,让我们提前两个多小时就出发了!' XPAT语法,需要另一个函数xpath的支持,它有4个参数:
还是上面的例子,不过这次使用XPATH语法: print(r.html.xpath("//div[@id='menu']", first=True).text)print(r.html.xpath("//div[@id='menu']/a"))print(r.html.xpath("//div[@class='content']/span/text()")) 输出和上面那个几乎一样,之所以说是“几乎”,因为第三个输出会多出几个换行符,不知道什么原因。需要注意的一点是如果XPATH中包含 ['\n\n\n我一份文件忘家里了,又懒得回家取,就给小姨子发短信息: 帮我把文件送来,晚上我谢谢你。等半天也没送来文件,我只好打个车回家自己拿,到家一进屋,我就发现气氛不对劲,老婆铁青着脸,两手掐着腰,小姨子站旁边对我怒目而视。'] 元素内容糗事百科首页LOGO的HTML代码如下所示: <div class="logo" id="hd_logo"> <a href="/"><h1>糗事百科</h1></a> </div> 我们来选取这个元素: e = r.html.find("div#hd_logo", first=True) 要获取元素的文本内容,用text属性: print(e.text)# 糗事百科 要获取元素的attribute,用attr属性: print(e.attrs)# {'class': ('logo',), 'id': 'hd_logo'} 要获取元素的HTML代码,用html属性: print(e.html)# <div class="logo" id="hd_logo"># <a href="/"><h1>糗事百科</h1></a># </div> 要搜索元素的文本内容,用search函数,比如说我们现在想知道是糗事什么科: print(e.search("糗事{}科")[0])# 百 最后还有前面提到的两个链接属性: print(e.absolute_links)print(e.links)# {'https://www./'}# {'/'} print(e.absolute_links)print(e.links)# {'https://www./'}# {'/'} 三、进阶用法JavaScript支持有些网站是使用JavaScript渲染的,这样的网站爬取到的结果只有一堆JS代码,这样的网站requests-html也可以处理,关键一步就是在HTML结果上调用一下render函数,它会在用户目录(默认是 render函数还有一些参数,顺便介绍一下(这些参数有的还有默认值,直接看源代码方法参数列表即可):
比如说简书的用户页面上用户的文章列表就是一个异步加载的例子,初始只显示最近几篇文章,如果想爬取所有文章,就需要使用scrolldown配合sleep参数模拟下滑页面,促使JS代码加载所有文章。 智能分页有些网站会分页显示内容,例如reddit。 >>> r = session.get('https://') >>> for html in r.html: ... print(html) <HTML url='https://www./'> <HTML url='https://www./?count=25&after=t3_81puu5'> <HTML url='https://www./?count=50&after=t3_81nevg'> <HTML url='https://www./?count=75&after=t3_81lqtp'> <HTML url='https://www./?count=100&after=t3_81k1c8'> <HTML url='https://www./?count=125&after=t3_81p438'> <HTML url='https://www./?count=150&after=t3_81nrcd'> … 请求下一个网页就很容易了 >>> r = session.get('https://') >>> r.html.next()'https://www./?count=25&after=t3_81pm82' 直接使用HTML前面介绍的都是通过网络请求HTML内容 >>> from requests_html import HTML >>> doc = """<a href='https://'>""" 直接渲染JS代码也可以: # 和上面一段代码接起来>>> script = """ () => { return { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight, deviceScaleFactor: window.devicePixelRatio, } } """>>> val = html.render(script=script, reload=False) 自定义请求前面都是简单的用GET方法获取请求,如果需要登录等比较复杂的过程,就不能用get方法了。 自定义用户代理有些网站会使用UA来识别客户端类型,有时候需要伪造UA来实现某些操作。如果查看文档的话会发现 from pprint import pprint r = session.get('http:///get') pprint(json.loads(r.html.html)) 返回结果如下: {'args': {}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Host': '', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) ' 'AppleWebKit/603.3.8 (KHTML, like Gecko) ' 'Version/10.1.2 Safari/603.3.8'}, 'origin': '110.18.237.233', 'url': 'http:///get'} 可以看到UA是requests-html自带的UA,下面换一个UA: ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0'r = session.get('http:///get', headers={'user-agent': ua}) pprint(json.loads(r.html.html)) 可以看到UA确实发生了变化: {'args': {}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Host': '', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) ' 'Gecko/20100101 Firefox/62.0'}, 'origin': '110.18.237.233', 'url': 'http:///get'} 当然这里仅仅是换了一个UA,如果你有需要可以在header中修改其他参数。 模拟表单登录
# 表单登录r = session.post('http:///post', data={'username': 'yitian', 'passwd': 123456}) pprint(json.loads(r.html.html)) 结果如下,可以看到forms中确实收到了提交的表单值: {'args': {}, 'data': '', 'files': {}, 'form': {'passwd': '123456', 'username': 'yitian'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Content-Length': '29', 'Content-Type': 'application/x-www-form-urlencoded', 'Host': '', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) ' 'AppleWebKit/603.3.8 (KHTML, like Gecko) ' 'Version/10.1.2 Safari/603.3.8'}, 'json': None, 'origin': '110.18.237.233', 'url': 'http:///post'} 如果有上传文件的需要,做法也是类似的。如果了解过requests库的同学可能对这里的做法比较熟悉,没有错,这其实就是requests的用法。requests-html通过暴露 前言requests虽好,但有个遗憾,它无法加载JavaScript,当访问一个url地址的时候,不能像selenium一样渲染整个html页面出来。 JavaScript支持当第一次使用render() 渲染页面的时候,会自动下载chromium,但只会下载这一次,后面就不会下载了。
render()渲染页面到底渲染html页面是个什么概念呢?可以请求之后对比抓包看下,不使用render()之前,只发一个请求 使用render()之后,会发很多请求,类型于手工在浏览器上输入url后,浏览器渲染整个完整的页面,这正是我们想要的模拟浏览器发请求 案例接下来访问我的博客地址后,抓取我的个人信息
打印结果
困扰很久的问题终于找到了解决办法,更多强大的功能可以去requests-html的GitHub地址https://github.com/kennethreitz/requests-html |
|