通常我們從網頁上爬取內容時,都是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中的信息: