前幾天寫了個java爬蟲爬花瓣網,可是過後總感受不夠舒服,終於在今天下午寫了個python爬蟲(爬微博圖片滴),寫完以後就感受舒服了,果真爬蟲就應該用python來寫,哈哈(這裏開個玩笑,非引戰言論)。話很少說進入正題。html
我以前去網上搜了一圈爬微博的爬蟲大都是採用模擬登錄的方式爬取,我這裏並無採用那種方式,直接是經過模擬請求獲得數據的。以下(爬取的微博:https://m.weibo.cn/profile/1792328230)java
這個頁面是該博主的我的簡介頁面,直接拉到底,會有一個查看全部微博,點擊它會跳轉到該博主的全部微博頁面python
這裏打開開發者工具查看網絡請求,找到這個數據接口https://m.weibo.cn/api/container/getIndex?containerid=2304131792328230_-_WEIBO_SECOND_PROFILE_WEIBO,你會發現本頁面全部內容都在該請求返回的json數據包中。git
接着往下滑頁面繼續觀察該請求窗口,就會發現這個接口的參數的規律。發現規律後就是老一套的模擬ajax加載獲取多頁數據,而後爬取目標內容。該數據接口參數以下:github
https://m.weibo.cn/api/container/getIndex?containerid=?&page=? 其中參數id是指定該用戶,page就是頁數(默認從1開始).ajax
(json數據可自行觀察規律,很容易找到要爬的數據所在)json
建立一個WbGrawler類,並在構造方法初始化固定參數,以下:api
class WbGrawler(): def __init__(self): """ 參數的初始化 :return: """ self.baseurl = "https://m.weibo.cn/api/container/getIndex?containerid=2304131792328230&" self.headers = { "Host": "m.weibo.cn", "Referer": "https://m.weibo.cn/p/2304131792328230", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36", "X-Requested-with": "XMLHttpRequest" } # 圖片保存路徑 self.path = "D:/weibosrc/"
而後去寫一個獲取單個頁面json數據的方法,由於變化的參數只有page,因此這裏傳入一個page便可,以下:網絡
def getPageJson(self,page): """ 獲取單個頁面的json數據 :param page:傳入的page參數 :return:返回頁面響應的json數據 """ url = self.baseurl + "page=%d"%page try: response = requests.get(url,self.headers) if response.status_code==200: return response.json() except requests.ConnectionError as e: print("error",e.args)
拿到json數據後就要開始解析它並獲得目標數據,因此這裏寫一個解析json數據的方法,傳入一個json參數,以下:多線程
def parserJson(self, json): """ 解析json數據獲得目標數據 :param json: 傳入的json數據 :return: 返回目標數據 """ items = json.get("data").get("cards") for item in items: pics = item.get("mblog").get("pics") picList = [] # 有些微博沒有配圖,因此須要加一個判斷,方便後面遍歷不會出錯 if pics is not None: for pic in pics: pic_dict = {} pic_dict["pid"] = pic.get("pid") pic_dict["url"] = pic.get("large").get("url") picList.append(pic_dict) yield picList
這裏返回的是一個個列表,列表裏面的元素是存儲圖片信息的字典,獲得圖片信息後就能夠開始下載了(最使人興奮的下載環節),以下:
def imgDownload(self,results): """ 下載圖片 :param results: :return: """ for result in results: for img_dict in result: img_name = img_dict.get("pid") + ".jpg" try: img_data = requests.get(img_dict.get("url")).content with open(self.path+img_name,"wb") as file: file.write(img_data) file.close() print(img_name+"\tdownload successed!") except Exception as e: print(img_name+"\tdownload failed!",e.args)
一開始試着爬了一頁爬了很久(多是電腦性能或者網絡的問題),因此想着能不能開啓多線程爬取,可是用戶輸入的頁數是未知的,開啓多少個子線程貌似很難取捨,這裏我想到了在java裏學的一個知識點--線程池,對利用線程池的特色就能徹底規避掉這個尷尬。python線程池可參考這篇博客:https://www.cnblogs.com/xiaozi/p/6182990.html
def startCrawler(self,page): page_json = self.getPageJson(page) results = self.parserJson(page_json) self.imgDownload(results)
if __name__ == '__main__': wg = WbGrawler() pool = threadpool.ThreadPool(10) reqs = threadpool.makeRequests(wg.startCrawler,range(1,5)) [pool.putRequest(req) for req in reqs] pool.wait()
最後就沒了,源碼:https://github.com/zengtao614/WbCrawler ,上文若有錯誤或者疏漏之處歡迎指出,萬分感謝。