【爬蟲入門】抓取今日頭條的街拍搜尋頁的圖片,並儲存到資料庫和本地
阿新 • • 發佈:2019-01-11
使用多程序對街拍圖片進行下載,並將圖片相關資訊儲存到mongodb資料庫中。
import requests, re, json, pymongo from multiprocessing import Pool from urllib.parse import urlencode from hashlib import md5 class JiePaiSpider(object): # 程序池無法序列化pymongo物件,因為pymongo資料庫中含有執行緒鎖。 # TypeError: can't pickle _thread.lock objects # 建立pymongo的連結 client = pymongo.MongoClient('localhost') db = client['jiepai'] def __init__(self): self.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.12 Safari/537.36' } def get_list_json(self, offset): """ 請求列表頁的json介面,獲取列表頁中的圖片資訊。 :param offset: 請求介面時的偏移量引數。(0, 20, 40....) :return: """ # https://www.toutiao.com/search_content/?offset=0&format=json&keyword=%E8%A1%97%E6%8B%8D&autoload=true&count=20&cur_tab=1&from=search_tab&pd=synthesis # 準備介面引數 params = { 'offset': offset, 'format': 'json', 'keyword': '街拍', 'autoload': 'true', 'count': '20', 'cur_tab': '1', 'from': 'search_tab', 'pd': 'synthesis' } api_url = 'https://www.toutiao.com/search_content/?' + urlencode(params) try: response = requests.get(api_url, headers=self.headers) if response.status_code == 200: # 響應狀態碼是200,說明GET請求成功 return response.text else: print('請求異常:url={}, status_code={}'.format(api_url, response.status_code)) return None except Exception as e: print('請求異常:url={}, error={}'.format(api_url, e)) return None def parse_list_json(self, json_str): """ 解析列表頁json資料 :param json_str: :return: """ json_dict = json.loads(json_str) if 'data' in json_dict.keys(): # 判斷字典json_dict的所有鍵中是否包含'data',如果有,可以解析,如果沒有,可能沒有資料或者發生異常了。 data_list = json_dict.get('data', None) if data_list and len(data_list) > 0: # 說明還有資料,可以解析 urls = [] for item in data_list: if 'single_mode' not in item and 'cell_type' not in item: article_url = item['article_url'] urls.append(article_url) return urls def get_detail_page(self, detail_url): try: response = requests.get(detail_url, headers=self.headers) if response.status_code == 200: # 響應狀態碼是200,說明GET請求成功 return response.text else: print('請求異常:url={}, status_code={}'.format(detail_url, response.status_code)) return None except Exception as e: print('請求異常:url={}, error={}'.format(detail_url, e)) return None def parse_detail_page(self, detail_html): # \(:表示對正則表示式中的(進行轉義,轉化為一個普通的字元。 js_json_str = re.findall(re.compile(r'gallery: JSON\.parse\((.*?)\),', re.S), detail_html)[0].replace('\\', '').strip('"') # 資料儲存到Mongo中 data_dict = json.loads(js_json_str) self.save_dict_to_db(data_dict) # 解析Json,取出圖片的url地址,下載圖片到本地。 for item_dict in data_dict['sub_images']: img_url = item_dict['url'] # 根據圖片url地址,下載圖片 self.download_image(img_url) def download_image(self, img_url): response = requests.get(img_url, headers=self.headers) if response.status_code == 200: # response.text: 獲取的是文字資源;(json字串、網頁原始碼) # 但是圖片屬於二進位制資源,圖片資料的傳輸是以二進位制流的形式傳輸的,不再是字串了。 content = response.content # md5()函式的引數需要是一個bytes位元組碼,不能是str型別的字串。 # hexdigest(): 獲取md5加密後的結果。 img_name = md5(img_url.encode('utf-8')).hexdigest() # 'w': 寫入普通文字;'wb': 專門用於寫入二進位制資料(圖片、音訊、視訊) f = open('imgs/{}.jpg'.format(img_name), 'wb') f.write(content) f.close() else: print('圖片請求失敗:{}'.format(img_url)) def save_dict_to_db(self, dic): self.db['img'].insert_one(dic) def start_spider(self, offset): print('正在請求偏移量為{}的圖片'.format(offset)) json_str = self.get_list_json(offset) if json_str: urls = self.parse_list_json(json_str) for detail_url in urls: detail_html = self.get_detail_page(detail_url) if detail_html: self.parse_detail_page(detail_html) if __name__ == '__main__': jp = JiePaiSpider() pool = Pool(3) pool.map(jp.start_spider, [x for x in range(0, 101) if x % 20 == 0]) # jp.start_spider(0) pool.close() pool.join()