分析Ajax請求並抓取今日頭條街拍美圖


  通常我們從網頁上爬取內容時,都是HTML代碼,內容都已經寫好了,直接從頁面獲取想要的信息即可,但是有的網頁是通過ajax獲取的數據,將ajax獲取的數據通過json格式接受,然后展示在頁面上的,也就是說,當我們打開一個頁面時,首先請求的是他的html,然后HTML里面通過ajax獲取后端數據,將數據以json格式展示在頁面上。而近日頭條就是這樣。下面我們來看看。

  我們打開鏈接:https://www.toutiao.com/search/?keyword=%E8%A1%97%E6%8B%8D,我們發現下面圖片的鏈接和我們粘貼復制過來的地址不一樣,說明最終請求的數據通過了ajax請求,數據格式發生了改變。我們查看網頁源碼,發現頁面中沒有關於我們在頭條中看到的信息。

我們使用開發者選項F12,查看網頁元素,發現請求頭header中url與我們上面的連接不一樣,增加了很多信息

我們查看請求的參數,發現有很多參數,這就是通過ajax真正訪問的數據連接,我們需要的就是將https://www.toutiao.com/search_content/?   +  加上下面的各種參數,就能查看到真正的數據源,

下面是請求網頁源數據的代碼:

#請求網頁具體信息的函數
def get_page_index(offset,keyword):
    data={
        'offset': offset,
        'format': 'json',
        'keyword': keyword,
        'autoload': 'true',
        'count': '20',
        'cur_tab':3,
        'from': 'search_tab'
    }
    url='https://www.toutiao.com/search_content/?'+urlencode(data)
    try:
        response = requests.get(url)
        if response.status_code == 200:
            return response.text
        return None
    except RequestException:
        return None

接下來應該解析網頁中的數據:我們通過上面的請求方式獲得的就是通過ajax請求返回的json數據,這些數據中我們需要每一條頭條的鏈接,下面是解析函數:

def parse_page_index(html):
    data = json.loads(html)#將獲取的ajax請求轉換為json類型的數據
    if data and 'data' in data.keys():
        for item in data.get('data'):
            yield item.get('article_url')#從json數據中獲取名為artile_url的數據

接下來我們需要根據上面獲得url請求每一條頭條的內容:

def get_page_datail(url):
    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'
    }
    try:
        response = requests.get(url,headers=headers)
        if response.status_code == 200:
            return response.text
        return None
    except RequestException:
        print('出現錯誤')
        return None

注意:這里我們添加了請求頭,添加請求頭的原因是有的網頁防止python爬取數據,我們可以通過偽代理或者添加header的方式來避免。

 

我們打開頭條中的一條新聞,查看網頁源代碼,發現同樣的我們看不到這條消息的數據,這些數據都包含在ajax請求返回的json數據中,我們需要從這些數據中提取

我們需要從網頁中解析,下面是代碼:

def pase_page_detail(html,url):
    soup=BeautifulSoup(html,'lxml')#使用beautifulsoup解析庫來解析網頁
    title = soup.select('title')[0].get_text()#直接可以通過標簽名來獲取頭條的名稱
    print(title)
    images_pattern  = re.compile('BASE_DATA.galleryInfo.*?gallery: JSON.parse\("(.*?)"\),',re.S)#這里我們使用正則表達式獲取我們想要圖片的信息
    result = re.search(images_pattern,html)
    if not result is None:#有的網頁中不含有符合正則表達式的內容,我們直接將之過濾
        data=json.loads(result.group(1).replace('\\',''))#將\\轉義替換為空字符,同時將圖片的信息轉為json格式
        if data and 'sub_images'in data.keys():#判斷數據不為空並且里面含有圖片的信息
            sub_images=data.get('sub_images')#從json數據中獲取名為sub_images的內容
            images=[item.get('url') for item in sub_images]#有的新聞中含有多張圖片,我們將圖片的鏈接存在數組中
            for image in images:
                get_save_image(image)#請求圖片的鏈接
            return{
                'title':title,
                'url':url,
                'image':images
            }#將我們得到的數據以數組格式返回,需要標題,url,還有圖片鏈接

接下來我們需要將這些數據存放到MongoDB數據庫,首先定義幾個靜態變量,新建一個config.py的python文件

MONGO_URL='localhost'#數據庫的ip就是localhost
MONGO_DB='toutiao'#存放的數據庫名稱
MONGO_TABLE='toutiao'#存放的表名稱

在寫代碼的python文件中定義數據庫:

client = pymongo.MongoClient(MONGO_URL,connect=False)
db = client[MONGO_DB]

下面是存放到MongoDB的函數:

def save_to_mongo(result):
    if db[MONGO_TABLE].insert(result):
        print('存儲到數據庫成功',result)
        return True
    return False

我們還有將圖片保存在本地的函數,這里我直接上全部代碼:

import json
import os
import re
from hashlib import md5
from multiprocessing import Pool

from urllib.parse import urlencode

import pymongo

from config import *
client = pymongo.MongoClient(MONGO_URL,connect=False)
db = client[MONGO_DB]

from bs4 import BeautifulSoup
from requests.exceptions import RequestException
import requests

#請求網頁具體信息的函數
def get_page_index(offset,keyword):
    data={
        'offset': offset,
        'format': 'json',
        'keyword': keyword,
        'autoload': 'true',
        'count': '20',
        'cur_tab':3,
        'from': 'search_tab'
    }
    url='https://www.toutiao.com/search_content/?'+urlencode(data)
    try:
        response = requests.get(url)
        if response.status_code == 200:
            return response.text
        return None
    except RequestException:
        return None

def parse_page_index(html):
    data = json.loads(html)#將獲取的ajax請求轉換為json類型的數據
    if data and 'data' in data.keys():
        for item in data.get('data'):
            yield item.get('article_url')#從json數據中獲取名為artile_url的數據

def get_page_datail(url):
    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'
    }
    try:
        response = requests.get(url,headers=headers)
        if response.status_code == 200:
            return response.text
        return None
    except RequestException:
        print('出現錯誤')
        return None

def pase_page_detail(html,url):
    soup=BeautifulSoup(html,'lxml')#使用beautifulsoup解析庫來解析網頁
    title = soup.select('title')[0].get_text()#直接可以通過標簽名來獲取頭條的名稱
    print(title)
    images_pattern  = re.compile('BASE_DATA.galleryInfo.*?gallery: JSON.parse\("(.*?)"\),',re.S)#這里我們使用正則表達式獲取我們想要圖片的信息
    result = re.search(images_pattern,html)
    if not result is None:#有的網頁中不含有符合正則表達式的內容,我們直接將之過濾
        data=json.loads(result.group(1).replace('\\',''))#將\\轉義替換為空字符,同時將圖片的信息轉為json格式
        if data and 'sub_images'in data.keys():#判斷數據不為空並且里面含有圖片的信息
            sub_images=data.get('sub_images')#從json數據中獲取名為sub_images的內容
            images=[item.get('url') for item in sub_images]#有的新聞中含有多張圖片,我們將圖片的鏈接存在數組中
            for image in images:
                get_save_image(image)#請求圖片的鏈接
            return{
                'title':title,
                'url':url,
                'image':images
            }#將我們得到的數據以數組格式返回,需要標題,url,還有圖片鏈接

def save_to_mongo(result):
    if db[MONGO_TABLE].insert(result):
        print('存儲到數據庫成功',result)
        return True
    return False

def get_save_image(url):
    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'
    }
    try:
        response = requests.get(url, headers=headers)
        if response.status_code == 200:
            save_image(response.content)
        return None
    except RequestException:
        print('請求圖片界面錯誤')
        return None
def save_image(content):
    file_path='{0}/{1}.{2}'.format(os.getcwd(),md5(content).hexdigest(),'jpg')
    if not os._exists(file_path):
        with open(file_path,'wb')as f:
            f.write(content)
            f.close()


def main(offset):
    html=get_page_index(offset,'街拍')
    for url in parse_page_index(html):
        if url:
            html1=get_page_datail(url)
            if html:
                result=pase_page_detail(html1,url)
                if not result is None:
                    save_to_mongo(result)


if __name__  == '__main__':
    groups = [x*2 for x in range(GROUP_START,GROUP_END+1)]
    pool = Pool()
    pool.map(main,groups)

 

下面就是在MongoDB中的信息:

 


免責聲明!

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



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