一 介紹Python上有一個非常著名的HTTP庫——requests,相信大家都聽說過,用過的人都說非常爽!現(xiàn)在requests庫的作者又發(fā)布了一個新庫,叫做requests-html,看名字也能猜出來,這是一個解析HTML的庫,具備requests的功能以外,還新增了一些更加強大的功能,用起來比requests更爽!接下來我們來介紹一下它吧。 # 官網(wǎng)解釋'''This library intends to make parsing HTML (e.g. scraping the web) as simple and intuitive as possible. 官網(wǎng)告訴我們,它比原來的requests模塊更加強大,并且為我們提供了一些新的功能!
二 安裝安裝requests-html非常簡單,一行命令即可做到。需要注意一點就是,requests-html只支持Python 3.6或以上的版本,所以使用老版本的Python的同學(xué)需要更新一下Python版本了。 # pip3 install requests-html三 如何使用requests-html?在我們學(xué)爬蟲程序的時候用得最多的請求庫就是requests與urllib,但問題是這些包只給我們提供了如何去目標(biāo)站點發(fā)送請求,然后獲取響應(yīng)數(shù)據(jù),接著再利用bs4或xpath解析庫才能提取我們需要的數(shù)據(jù)。 以往爬蟲的請求與解析而在requests-html里面只需要一步就可以完成而且可以直接進行js渲染!requests的作者Kenneth Reitz 開發(fā)的requests-html 爬蟲包 是基于現(xiàn)有的框架 PyQuery、Requests、lxml、beautifulsoup4等庫進行了二次封裝,作者將Requests的簡單,便捷,強大又做了一次升級。 requests-html和其他解析HTML庫最大的不同點在于HTML解析庫一般都是專用的,所以我們需要用另一個HTTP庫先把網(wǎng)頁下載下來,然后傳給那些HTML解析庫。而requests-html自帶了這個功能,所以在爬取網(wǎng)頁等方面非常方便。 1、基本使用from requests_html import HTMLSession 2、獲取鏈接(links與abolute_links)links返回的結(jié)果 absolute_links返回的結(jié)果
from requests_html import HTMLSession 3、CSS選擇器與XPATHrequest-html支持CSS選擇器和XPATH兩種語法來選取HTML元素。首先先來看看CSS選擇器語法,它需要使用HTML的 find 函數(shù)來查找元素。 ''' CSS選擇器 and XPATH 1.通過css選擇器選取一個Element對象 2.獲取一個Element對象內(nèi)的文本內(nèi)容 3.獲取一個Element對象的所有attributes 4.渲染出一個Element對象的HTML內(nèi)容 5.獲取Element對象內(nèi)的特定子Element對象,返回列表 6.在獲取的頁面中通過search查找文本 7.支持XPath 8.獲取到只包含某些文本的Element對象''' view code四 支持JavaScript支持JavaScript是我覺得作者更新后最為牛逼的一個地方,但是需要在第一次執(zhí)行render的時候下載chromeium,然后通過它來執(zhí)行js代碼。 1、render的使用from requests_html import HTMLSession 注意:第一次運行render()方法時,它會將Chromium下載到您的主目錄中(例如~/.pyppeteer/)。這種情況只發(fā)生一次。 2、 下載Chromeium問題因為是從國外的站點下載幾分鐘才3%,實在是太慢了。所以我們需要通過國內(nèi)的鏡像去下載!需要做以下幾步:
五 自定義User-Agent 有些網(wǎng)站會使用User-Agent來識別客戶端類型,有時候需要偽造UA來實現(xiàn)某些操作。如果查看文檔的話會發(fā)現(xiàn) from requests_html import HTMLSession# pprint可以把數(shù)據(jù)打印得更整齊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))''' # 打印結(jié)果{'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內(nèi)部就封裝好了aynsc異步請求的功能,可以提高我們的爬蟲效率。 from requests_html import AsyncHTMLSessionfrom requests_html import HTMLSessionimport time 1. 開始Python 中可以進行網(wǎng)頁解析的庫有很多,常見的有 BeautifulSoup 和 lxml 等。在網(wǎng)上玩爬蟲的文章通常都是介紹 BeautifulSoup 這個庫,我平常也是常用這個庫,最近用 Xpath 用得比較多,使用 BeautifulSoup 就不大習(xí)慣,很久之前就知道 Reitz 大神出了一個叫 Requests-HTML 的庫,一直沒有興趣看,這回可算歹著機會用一下了。 使用 from requests_html import HTMLSession 這個庫是在 requests 庫上實現(xiàn)的,r 得到的結(jié)果是 Response 對象下面的一個子類,多個一個 r.html2. 原理不得不膜拜 Reitz 大神太會組裝技術(shù)了。實際上 HTMLSession 是繼承自 requests.Session 這個核心類,然后將 requests.Session 類里的 requests 方法改寫,返回自己的一個 HTMLResponse 對象,這個類又是繼承自 requests.Response,只是多加了一個 class HTMLSession(requests.Session): class HTMLResponse(requests.Response): 之后在 HTMLResponse 里定義屬性方法 html,就可以通過 html 屬性訪問了,實現(xiàn)也就是組裝 PyQuery 來干。核心的解析類也大多是使用 PyQuery 和 lxml 來做解析,簡化了名稱,挺討巧的。 3. 元素定位元素定位可以選擇兩種方式: css 選擇器
# css 獲取有多少個職位 方法名非常簡單,符合 Python 優(yōu)雅的風(fēng)格,這里不妨對這兩種方式簡單的說明: 4. CSS 簡單規(guī)則
5. Xpath簡單規(guī)則
定位到元素以后勢必要獲取元素里面的內(nèi)容和屬性相關(guān)數(shù)據(jù),獲取文本: jobs.text 獲取元素的屬性: attrs = jobs.attrs 還可以通過模式來匹配對應(yīng)的內(nèi)容: ## 找某些內(nèi)容匹配 這個功能看起來比較雞肋,可以深入研究優(yōu)化一下,說不定能在 github 上混個提交。 6. 人性化操作除了一些基礎(chǔ)操作,這個庫還提供了一些人性化的操作。比如一鍵獲取網(wǎng)頁的所有超鏈接,這對于整站爬蟲應(yīng)該是個福音,URL 管理比較方便: r.html.absolute_links 內(nèi)容頁面通常都是分頁的,一次抓取不了太多,這個庫可以獲取分頁信息: print(r.html) 結(jié)果如下: # print(r.html) 通過迭代器實現(xiàn)了智能發(fā)現(xiàn)分頁,這個迭代器里面會用一個叫 def get_next(): 通過查找 a 標(biāo)簽里面是否含有指定的文本來判斷是不是有下一頁,通常我們的下一頁都會通過 7. 加載 js也許是考慮到了現(xiàn)在 js 的一些異步加載,這個庫支持 js 運行時,官方說明如下:
使用非常簡單,直接調(diào)用以下方法: r.html.render() 第一次使用的時候會下載 Chromium,不過國內(nèi)你懂的,自己想辦法去下吧,就不要等它自己下載了。render 函數(shù)可以使用 js 腳本來操作頁面,滾動操作單獨做了參數(shù)。這對于上拉加載等新式頁面是非常友好的。 8. 總結(jié)Reitz 大神設(shè)計出來的東西還是一如既往的簡單好用,自己不多做,大多用別人的東西組裝,簡化 api。真是夠人性。不過有的地方還是優(yōu)化空間,希望有興趣和精力的童鞋去 github 上關(guān)注一下這個項目。 昨天寫了requests庫好!最近requests庫的作者又發(fā)布了一個新庫,叫做requests-html,看名字也能猜出來,這是一個解析HTML的庫,而且用起來和requests一樣爽,下面就來介紹一下它。 一、安裝pip install requests-html 二、基本使用獲取網(wǎng)頁from requests_html import HTMLSession 獲取鏈接
# 獲取鏈接print(r.html.links)print(r.html.absolute_links) 結(jié)果如下 {'/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函數(shù),該函數(shù)有5個參數(shù),作用如下:
例子: # 首頁菜單文本print(r.html.find('div#menu', first=True).text)# 首頁菜單元素print(r.html.find('div#menu a'))# 段子內(nèi)容print(list(map(lambda x: x.text, r.html.find('div.content span')))) 結(jié)果如下, 熱門 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/'>] ['有一次,幾位大城市的朋友來家里玩,我招待他們吃風(fēng)干羊肉做臊子的饸饹面,這是我們老家最具特色的美食!飯快熟的時候,老婆讓我在園子里摘點“芫荽 ”,朋友問我,“芫荽”是什么東東?我給他們翻譯解釋說:我們本地土話叫“芫荽”,你們城里人講普通話叫香菜,他們還大笑了一場。\n前天下雨沒事兒干,翻看新華字典,突然發(fā)現(xiàn)“芫荽”才是香菜的學(xué)名,Tm香菜才是土話!而且我們地方方言就這兩個字發(fā)音還特別標(biāo)準(zhǔn)!', '昨天晚上跟老婆吵架,他抓起我的手機就摔了。我立馬摔了他的,結(jié)果我的還能用,他的壞了。高潮是人家立刻出門買了個新的!我艸,是不是中計了??', '小姨要去高鐵站,我看著大大小小的箱子說:坐公交車要轉(zhuǎn)車,轉(zhuǎn)來轉(zhuǎn)去要一個多小時,太不方便了,不如我開車送你吧。\n小姨遲疑了一下,同意了。\n我準(zhǔn)時把小姨送到了高鐵站,正好趕上檢票。\n小姨高興地說:自己開車就是方便,不過幸好你媽聰明,讓我們提前兩個多小時就出發(fā)了!' XPAT語法,需要另一個函數(shù)xpath的支持,它有4個參數(shù):
還是上面的例子,不過這次使用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我一份文件忘家里了,又懶得回家取,就給小姨子發(fā)短信息: 幫我把文件送來,晚上我謝謝你。等半天也沒送來文件,我只好打個車回家自己拿,到家一進屋,我就發(fā)現(xiàn)氣氛不對勁,老婆鐵青著臉,兩手掐著腰,小姨子站旁邊對我怒目而視。'] 元素內(nèi)容糗事百科首頁LOGO的HTML代碼如下所示: <div class="logo" id="hd_logo"> <a href="/"><h1>糗事百科</h1></a> </div> 我們來選取這個元素: e = r.html.find("div#hd_logo", first=True) 要獲取元素的文本內(nèi)容,用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> 要搜索元素的文本內(nèi)容,用search函數(shù),比如說我們現(xiàn)在想知道是糗事什么科: print(e.search("糗事{}科")[0])# 百 最后還有前面提到的兩個鏈接屬性: print(e.absolute_links)print(e.links)# {'https://www./'}# {'/'} print(e.absolute_links)print(e.links)# {'https://www./'}# {'/'} 三、進階用法JavaScript支持有些網(wǎng)站是使用JavaScript渲染的,這樣的網(wǎng)站爬取到的結(jié)果只有一堆JS代碼,這樣的網(wǎng)站requests-html也可以處理,關(guān)鍵一步就是在HTML結(jié)果上調(diào)用一下render函數(shù),它會在用戶目錄(默認(rèn)是 render函數(shù)還有一些參數(shù),順便介紹一下(這些參數(shù)有的還有默認(rèn)值,直接看源代碼方法參數(shù)列表即可):
比如說簡書的用戶頁面上用戶的文章列表就是一個異步加載的例子,初始只顯示最近幾篇文章,如果想爬取所有文章,就需要使用scrolldown配合sleep參數(shù)模擬下滑頁面,促使JS代碼加載所有文章。 智能分頁有些網(wǎng)站會分頁顯示內(nèi)容,例如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'> … 請求下一個網(wǎng)頁就很容易了 >>> r = session.get('https://') >>> r.html.next()'https://www./?count=25&after=t3_81pm82' 直接使用HTML前面介紹的都是通過網(wǎng)絡(luò)請求HTML內(nèi)容 >>> 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方法獲取請求,如果需要登錄等比較復(fù)雜的過程,就不能用get方法了。 自定義用戶代理有些網(wǎng)站會使用UA來識別客戶端類型,有時候需要偽造UA來實現(xiàn)某些操作。如果查看文檔的話會發(fā)現(xiàn) from pprint import pprint r = session.get('http:///get') pprint(json.loads(r.html.html)) 返回結(jié)果如下: {'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確實發(fā)生了變化: {'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'}當(dāng)然這里僅僅是換了一個UA,如果你有需要可以在header中修改其他參數(shù)。 模擬表單登錄
# 表單登錄r = session.post('http:///post', data={'username': 'yitian', 'passwd': 123456}) pprint(json.loads(r.html.html)) 結(jié)果如下,可以看到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庫的同學(xué)可能對這里的做法比較熟悉,沒有錯,這其實就是requests的用法。requests-html通過暴露 前言requests雖好,但有個遺憾,它無法加載JavaScript,當(dāng)訪問一個url地址的時候,不能像selenium一樣渲染整個html頁面出來。 JavaScript支持當(dāng)?shù)谝淮问褂胷ender() 渲染頁面的時候,會自動下載chromium,但只會下載這一次,后面就不會下載了。
render()渲染頁面到底渲染html頁面是個什么概念呢?可以請求之后對比抓包看下,不使用render()之前,只發(fā)一個請求
使用render()之后,會發(fā)很多請求,類型于手工在瀏覽器上輸入url后,瀏覽器渲染整個完整的頁面,這正是我們想要的模擬瀏覽器發(fā)請求
案例接下來訪問我的博客地址后,抓取我的個人信息
打印結(jié)果 困擾很久的問題終于找到了解決辦法,更多強大的功能可以去requests-html的GitHub地址https://github.com/kennethreitz/requests-html |
|
|