如何爬取js動態生成的頁面數據--案例


一、目標網頁及要求

目標網頁:

https://www.xuexi.cn/f997e76a890b0e5a053c57b19f468436/018d244441062d8916dd472a4c6a0a0b.html

要求:

  • 爬取頁面中的詳情頁文章標題、內容、發布時間、文章來源,存入本地mongodb數據庫
  • 同時在本地創建一個文件夾,在該文件夾下以文章標題.txt創建文本,寫入文章內容

2、目標頁面分析

以Chrome瀏覽器為例,通過F12打開抓包工具,按F5刷新下頁面,ctr+F進行全局搜索頁面中的內容,發現該頁面內容是通過ajax動態獲取的

Method:GET

url:https://www.xuexi.cn/lgdata/f997e76a890b0e5a053c57b19f468436/018d244441062d8916dd472a4c6a0a0b.json?_st=27037952

將響應內容通過新窗口打開(安裝過JSONView插件),可以很直觀的查看Json數據結構

通過分析發現,詳情頁鏈接都在title下的link中,並且分為:帶請求參數id和不帶請求參數兩種形式

3、詳情頁分析

接下來分別對這兩種形式的詳情頁鏈接進行訪問,繼續按照目標主頁頁面的分析步驟,發現數據是通過js動態加載的

攜帶請求參數id:

詳情頁url  :https://www.xuexi.cn/lgpage/detail/index.html?id=10942099116228124117&item_id=10942099116228124117

數據來源js:https://boot-source.xuexi.cn/data/app/10942099116228124117.js?callback=callback&_st=1622280445799

其它詳情頁數據來源js都是這種格式

規律如下:

通過詳情頁url提取id,然后_st參數是16開頭的13位數字,沒錯,就是時間戳*1000后的整數部分,這樣我們就能通過詳情頁url來找到其數據來源js的url

提取數據:

    • 首先通過正則提取出dict數據,然后通過jsonpath規則提取出目標數據
    • 標題:‘$..title’
    • 內容:'$..content',之后通過xpath提取p或者span標簽中的內容
    • 來源:'$..show_source'
    • 出版時間:'$..publishi_time'

不帶請求參數:

詳情頁url  :https://www.xuexi.cn/5c39c314138da31babf0b16af5a55da4/e43e220633a65f9b6d8b53712cba9caa.html

數據來源js:https://www.xuexi.cn/5c39c314138da31babf0b16af5a55da4/datae43e220633a65f9b6d8b53712cba9caa.js

其它詳情頁都是以e43e220633a65f9b6d8b53712cba9caa.html結尾,js頁都是以datae43e220633a65f9b6d8b53712cba9caa.js結尾

規律如下:

將詳情頁url中的e43e220633a65f9b6d8b53712cba9caa.html替換成datae43e220633a65f9b6d8b53712cba9caa.js即可

提取數據:

    • 首先通過正則提取出dict數據,然后通過jsonpath規則提取出目標數據
    • 標題:'$.fp8ttetzkclds001..frst_name'
    • 內容:'$.fp8ttetzkclds001.detail.content',之后通過xpath提取p或者span標簽中的內容
    • 來源:'$.fp8ttetzkclds001..source'
    • 出版時間:'$.fp8ttetzkclds001..original_time'

4、代碼實現

# coding:utf-8
import jsonpath import json import re import requests import time import os import pymongo import fake_useragent from lxml import etree class XuexiSpider: def __init__(self): ua = fake_useragent.UserAgent().random  # 生成隨機User-Agent
        self.headers = { 'User-Agent':ua } self.json_data_url = 'https://www.xuexi.cn/lgdata/f997e76a890b0e5a053c57b19f468436/018d244441062d8916dd472a4c6a0a0b.json?_st=27035433' #首頁數據來源
        self.mongoClient = pymongo.MongoClient('mongodb://localhost:27017') def work(self): '''開啟爬蟲'''
        if not os.path.exists('./data'): os.mkdir('./data') # 發送請求,獲取首頁dict數據
        dict_data = requests.get(self.json_data_url,headers=self.headers).json() # 提取詳情頁link
        detail_page_links = jsonpath.jsonpath(dict_data,'$..title.link') for link in detail_page_links: if link.endswith('html'): self.parse_link_no_id(link) else: self.parse_link_with_id(link) self.mongoClient.close() #關閉數據庫連接

    def parse_link_with_id(self,link): '''解析url中帶有id參數的鏈接''' id = re.findall(r'id=(\d+)',link)[0] #提取link中的id
        _st = str(time.time()*1000).split('.')[0] #構造_st:當前時間戳*1000后取整數部分,然后轉成str
        js_link = 'https://boot-source.xuexi.cn/data/app/{0}.js?callback=callback&_st={1}'.format(id,_st) response = requests.get(js_link,headers=self.headers).text response_dict_data = json.loads(re.findall(r'{.*}',response)[0]) title = jsonpath.jsonpath(response_dict_data,'$..title')[0] #詳情頁標題
        raw_content = jsonpath.jsonpath(response_dict_data,'$..content')[0] #有的頁面只有圖片或者視頻,content內容為''或者<!--{img:0}-->\n\n,所以需要做個判斷
        if not raw_content: content = ''
        elif raw_content.startswith('<!--'): content = ''
        else: tree = etree.HTML(raw_content) content_list = tree.xpath('//p/text()|//span/text()') #文本內容存放在p或者span標簽內
            content = '\n\n'.join(content_list) #詳情頁內容
        source = jsonpath.jsonpath(response_dict_data,'$..show_source')[0] #詳情頁文章來源
        publish_time = jsonpath.jsonpath(response_dict_data,'$..publish_time')[0] #詳情頁文章出版時間
        item = { 'title':title, 'content':content, 'source':source, 'publish_time':publish_time } self.pipeline(item) def parse_link_no_id(self,link): '''解析url中不帶有id參數的鏈接''' js_link = link.replace('e43e220633a65f9b6d8b53712cba9caa.html','datae43e220633a65f9b6d8b53712cba9caa.js') response = requests.get(js_link, headers=self.headers).text response_dict_data = json.loads(re.findall(r'{.*}', response)[0]) title = jsonpath.jsonpath(response_dict_data, '$.fp8ttetzkclds001..frst_name')[0]  # 詳情頁標題
        raw_content = jsonpath.jsonpath(response_dict_data, '$.fp8ttetzkclds001.detail.content')[0] #content內容可能為空字符串,所以需要做個判斷
        if not raw_content: content = ''
        elif raw_content.startswith('<!--'): content = ''
        else: tree = etree.HTML(raw_content) content_list = tree.xpath('//p/text()|//span/text()') content = '\n\n'.join(content_list)  # 詳情頁內容
        source = jsonpath.jsonpath(response_dict_data, '$.fp8ttetzkclds001..source')[0]  # 詳情頁文章來源
        publish_time = jsonpath.jsonpath(response_dict_data, '$.fp8ttetzkclds001..original_time')[0]  # 詳情頁文章出版時間
        item = { 'title': title, 'content': content, 'source': source, 'publish_time': publish_time } self.pipeline(item) def pipeline(self,item): '''存儲數據'''
        # 寫入本地文件,文件名:詳情頁標題.txt
        with open('./data/'+item['title']+'.txt','w',encoding='utf-8') as f: f.write(item['content']) #存入mongodb數據庫
        db = self.mongoClient.test #獲取數據庫
        collection = db.xuexi #獲取集合
        collection.insert_one(item) #集合中插入數據

if __name__ == '__main__': xuexi = XuexiSpider() xuexi.work()

由於爬取的數據不是很多,就沒有采用多線程或協程異步去實現代碼

爬取結果:

 

文本內容:

 

 至此,大功告成!!!

 

分析案例來源:https://www.cnblogs.com/bobo-zhang/p/10561617.html#4216119

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM