我們在用python進行爬蟲時,可能都遇到過有些網頁直接請求得到的HTML 程式碼裡面,並沒有我們需要的數據,也就是我們在瀏覽器中看到的內容。
這就是因為這些資訊是透過Ajax載入的,並且透過js渲染產生的。這時候我們就需要分析這個網頁的請求了。
上一篇跟大家講解了什麼是Cookie及模擬登入的操作流程,今天帶給大家的是如何分析網頁的Ajax請求。
什麼是Ajax
AJAX即「Asynchronous Javascript And XML」(非同步JavaScript和XML),是指一種創建互動式網頁應用的網頁開發技術。
AJAX = 非同步 JavaScript和XML(標準通用標記語言的子集)。
AJAX 是一種用於建立快速動態網頁的技術。
AJAX 是一種在無需重新載入整個網頁的情況下,能夠更新部分網頁的技術。
簡單的說就是網頁加載,瀏覽器網址列的網址並沒有變,是javascript異步加載的網頁,應該是ajax。 AJAX一般是透過XMLHttpRequest 物件介面發送請求的,XMLHttpRequest 一般被縮寫為 XHR。
分析果殼網站點
我們目標網站就以果殼網來進行分析。
我們可以看到這個網頁並沒有翻頁按鈕,而當我們一直往下拉請求,網頁會自動的給我們加載更多內容。但是,當我們觀察網頁url時,發現它並沒有隨著網頁的載入請求而改變。而當我們直接請求這個url時,顯然我們只能得到到第一頁的html內容。
那我們要怎麼取得所有頁面的資料呢?
我們在Chrome中開啟開發者工具(F12)。我們點選Network,點選XHR標籤。然後我們刷新網頁,往下拉請求。這時候我們就可以看到XHR標籤,在網頁每次載入的時候就會跳出一個請求。
我們點擊第一個請求,可以看到他的參數:
retrieve_type:by_subject limit:20 offset:18 -:1500265766286
在點擊第二個請求,參數如下:
retrieve_type:by_subject limit:20 offset:38 -:1500265766287
limit參數是網頁每一頁限制載入的文章數,offset就是頁數了。接著往下看,我們會發現每個請求的offset參數都會加 20。
我們接著看每個請求的回應內容,這是一個就是格式的資料。我們點開result鍵,可以看到一個 20 篇文章的資料資訊。這樣我們就成功找到我們需要的資訊位置了,我們可以在請求頭中看到存放json資料的url位址。 http://www.guokr.com/apis/minisite/article.json?retrieve_type=by_subject&limit=20&offset=18
爬取流程
分析Ajax請求取得每一頁的文章url資訊;解析每一篇文章,取得需要資料;將獲得的資料保存資料庫;開啟多進程,大量抓取。
開始我們的工具仍然使用requests請求,BeautifulSoup解析。
首先我們要透過分析Ajax請求,獲得所有頁的信息,透過對上面對網頁的分析,可以得到Ajax載入的json資料的URL位址為:
http://www. guokr.com/apis/minisite/article.json?retrieve_type=by_subject&limit=20&offset=18我們需要建構這個URL。
# 导入可能要用到的模块 import requests from urllib.parse import urlencode from requests.exceptions import ConnectionError # 获得索引页的信息 def get_index(offset): base_url = 'http://www.guokr.com/apis/minisite/article.json?' data = { 'retrieve_type': "by_subject", 'limit': "20", 'offset': offset } params = urlencode(data) url = base_url + params try: resp = requests.get(url) if resp.status_code == 200: return resp.text return None except ConnectionError: print('Error.') return None### ######我們把上面分析頁面得到的請求參數建構成一個字典data,然後我們可以手動的構造這個url,但是urllib庫已經給我們提供了一個編碼方法,我們直接使用,就可以構造出完整的url了。然後是標準的requests請求頁面內容。 ###
import json # 解析json,获得文章url def parse_json(text): try: result = json.loads(text) if result: for i in result.get('result'): # print(i.get('url')) yield i.get('url') except: pass###我們使用josn.loads方法解析json,將其轉換成json物件。然後直接透過字典的操作,獲得文章的url位址。這裡使用yield,每次請求返回一個url,降低記憶體的消耗。由於我在後面抓的時候跳出一個json解析的錯誤,這裡直接過濾就好。 ######這裡我們可以試著列印看看,是不是成功運行。 ###
既然获得了文章的url,那么对于获得文章的数据就显得很简单了。这里不在进行详细的叙述。我们的目标是获得文章的标题,作者和内容。
由于有的文章里面包含一些图片,我们直接过滤掉文章内容里的图片就好了。
from bs4 import BeautifulSoup # 解析文章页 def parse_page(text): try: soup = BeautifulSoup(text, 'lxml') content = soup.find('div', class_="content") title = content.find('h1', id="articleTitle").get_text() author = content.find('div', class_="content-th-info").find('a').get_text() article_content = content.find('div', class_="document").find_all('p') all_p = [i.get_text() for i in article_content if not i.find('img') and not i.find('a')] article = '\n'.join(all_p) # print(title,'\n',author,'\n',article) data = { 'title': title, 'author': author, 'article': article } return data except: pass
这里在进行多进程抓取的时候,BeautifulSoup也会出现一个错误,依然直接过滤。我们把得到的数据保存为字典的形式,方便保存数据库。
接下来就是保存数据库的操作了,这里我们使用Mongodb进行数据的存储。
import pymongo from config import * client = pymongo.MongoClient(MONGO_URL, 27017) db = client[MONGO_DB] def save_database(data): if db[MONGO_TABLE].insert(data): print('Save to Database successful', data) return True return False
我们把数据库的名字,和表名保存到config配置文件中,在把配置信息导入文件,这样会方便代码的管理。
最后呢,由于果壳网数据还是比较多的,如果想要大量的抓取,我们可以使用多进程。
from multiprocessing import Pool # 定义一个主函数 def main(offset): text = get_index(offset) all_url = parse_json(text) for url in all_url: resp = get_page(url) data = parse_page(resp) if data: save_database(data) if __name__ == '__main__': pool = Pool() offsets = ([0] + [i*20+18 for i in range(500)]) pool.map(main, offsets) pool.close() pool.join()
函数的参数offset就是页数了。经过我的观察,果壳网最后一页页码是 12758,有 637 页。这里我们就抓取 500 页。进程池的map方法和Python内置的map方法使用类似。
好了,对于一些使用Ajax加载的网页,我们就可以这么抓取了。
以上是面對JS渲染的網頁如何分析Ajax請求的詳細內容。更多資訊請關注PHP中文網其他相關文章!