Ajax簡介:ajax
var xmlhttp; if (windows.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari xmlhttp =new XMLHttpRequest(); } else {// code for IE6, IE5 xmlhttp =new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.open("POST","/ajax/",true); xmlhttp.send();
JavaScript 對 Ajax 最底層的實現,實際上就是新建了 XMLHttpRequest 對象,而後調用 onreadystatechange 屬性設置了監聽,而後調用 open()和 send ()方法向某個連接(也就是服務器)送了請求。用 Python 實現請求發送以後,能夠獲得響應結果,但這裏請求的發送變成 JavaScript 來完成。因爲設置了監聽,因此當服務器返回響應時, onreadystatechange 對應的方法便會被觸發,而後在這個方法裏面解析響應內容便可。數據庫
解析內容json
獲得響應以後,onreadystatechange 屬性對應的方法便會被觸發,此時利用 xmlhttp 的 responseText 屬性即可取到響應內容。相似於 Python 中利用 requests 向服務器發起請求,而後獲得響應的過程。那麼返回內容多是 HTML,多是 JSON,接下來只須要在方法中用 JavaScript 進一步處理便可。如:若是是 JSON 的話,能夠進行解析和轉化 windows
1、Ajax分析方法 api
2、Ajax 結果提取數組
這個內容是 JSON 格式,瀏覽器開發者工具自動作了解析以方便咱們查看。最關鍵的兩部分信息就是 cardlistlnfo 和 cards:cardlistlnfo包含一個比較重要的信息 total,實際上是微博的總數量,能夠根據這個數字來估算分頁數;cards 是一個列表,它包含 10 個元素。瀏覽器
from urllib.parse import urlencode import requests base_url = 'https://m.weibo.cn/api/container/getlndex?' headers = { 'Host': 'm.weibo.cn', 'Referer': 'https://m.weibo.cn/u/2830678474', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36', 'X-Requested-With': 'XMLHttpRequest', } def get_page(page): params = { 'type': 'uid', 'value': '2830678474', 'containerid': '1076032830678474', 'page': page } url = base_url + urlencode(params) try: response = requests.get(url, headers=headers) if response.status_code == 200: return response.json() except requests.ConnectionError as e: print( 'Error' , e.args) 也可:
from pyquery import PyQuery as pq import requests def getPage(page): url = 'https://m.weibo.cn/api/container/getIndex?'#在怎麼來?分析Ajax請求 hd = {"User-Agent": 'Mozilla'} # 模仿瀏覽器 params = {'type': 'uid', 'value': '2830678474', 'containerid': '1076032830678474', 'page': page} # 做爲參數增長到url中去 try: response = requests.get(url, headers=headers, params=params) response.raise_for_status() return response.json() # 解析爲JSON返回 --snip--
首先,定義 base_url 來表示請求的 URL 的前半部分。而後,構造參數字典,其中 type、value、containerid 是固定參數, page 是可變參數。調用 urlencode()方法將參數轉化爲 -- URL的 GET 請求參數,相似於 type=uid&value=2830678474&containerid=1076032830678474&page=2 這樣的形式。base_url 與參數拼合造成一個新的 URL。判斷響應的狀態碼,若是是 200 ,則直接調用 json()方法將內容解析爲JSON 返回,不然不返回任何信息。若是出現異常,則捕獲並輸出其異常信息。服務器
from pyquery import PyQuery as pq def parse_page(json): if json: items = json.get('data').get('cards') for item in items: item = item.get('mblog') if item == None: #注意:若是不添加返回None,可能由於有部分無返回值致使報錯 continue weibo = {} weibo['id'] = item.get('id') weibo['text'] = pq(item.get('text')).text() weibo['attitudes'] = item.get('attitudes_count') weibo['comments'] = item.get('comments_count') weibo['reposts'] = item.get('reposts_count') yield weibo
藉助 pyquery 將正文中的 HTML籤去掉。網絡
遍歷 page,將提取到的結果輸出:多線程
if __name__ == '__main__': for page in range(1,11): json = get_page(page) results = parse_page(json) for result in results: print(result)
from pymongo import MongoClient client = MongoClient() db = client['weibo'] collection =db['weibo'] def save_to_mongo(result): if collection.insert(result): print('Save to mongo')
3、分析 Ajax爬取今日頭條街拍美圖
import requests from urllib.parse import urlencode def get_page(offset): params = { 'offset': offset, 'format': 'json', 'keyword': '街拍', 'autoload': 'true', 'count': '20', 'cur_tab': '1', 'from': 'search_tab' } base_url = 'https://www.toutiao.com/search_content/?' url = base_url + urlencode(params) try: resp = requests.get(url) if codes.ok == resp.status_code: return resp.json() except requests.ConnectionError: return None
這裏用 urlencode()方法構造請求的 GET 參數,而後用 requests 請求這個連接,若是返回狀 態碼爲 200(ok),則調用 response 的 json()方法將結果轉爲 JSON 格式,而後返回。
再實現一個解析方法:提取每條數據的 image_list 字段中的每一張圖片連接,將圖片連接和圖片所屬的標題一併返回,此時能夠構造一個生成器。實現代碼:
def get_images(json): if json.get('data'): data = json.get('data') for item in data: if item.get('cell_type') is not None: continue title = item.get('title') images = item.get('image_list')for image in images: yield { 'image': 'https:' + image.get('url'), 'title': title }
接下來,實現一個保存圖片的方法 save_image(),其中 item 是前面 get_images()方法返回的字典。首先根據 item 的 title 來建立文件夾,而後請求圖片連接,獲取圖片的二進制數據,以二進制的形式寫入文件。圖片的名稱可使用其內容的 MD5 值,這樣能夠去除重複。代碼:
import os from hashlib import md5 def save_image(item): img_path = 'img' + os.path.sep + item.get('title') if not os.path.exists(img_path): os.makedirs(img_path) try: resp = requests.get(item.get('image')) if codes.ok == resp.status_code: file_path = img_path + os.path.sep + '{file_name}.{file_suffix}'.format( file_name=md5(resp.content).hexdigest(), file_suffix='jpg') if not os.path.exists(file_path): with open(file_path, 'wb') as f: f.write(resp.content) print('Downloaded image path is %s' % file_path) else: print('Already Downloaded', file_path) except requests.ConnectionError: print('Failed to Save Image,item %s' % item)
最後,只須要構造一個時fset 數組,遍歷 offset ,提取圖片連接,並將其下載:
from multiprocessing.pool import Pool def main(offset): json = get_page(offset) for item in get_images(json): print(item) save_image(item) GROUP_START = 0 GROUP_END = 7 if __name__ == '__main__': pool = Pool() groups = ([x * 20 for x in range(GROUP_START, GROUP_END + 1)]) pool.map(main, groups) pool.close() pool.join()
定義分頁的起始頁數和終止頁數,分別爲 GROUP_START和 GROUP_END ,利用了多線程的線程池,調用其 map()方法實現多線程下載。