前幾天寫了個java爬蟲爬花瓣網,但是事后總感覺不夠舒服,終於在今天下午寫了個python爬蟲(爬微博圖片滴),寫完之后就感覺舒服了,果然爬蟲就應該用python來寫,哈哈(這里開個玩笑,非引戰言論)。話不多說進入正題。
1.分析頁面
我之前去網上搜了一圈爬微博的爬蟲大都是采用模擬登陸的方式爬取,我這里並沒有采用那種方式,直接是通過模擬請求得到數據的。如下(爬取的微博:https://m.weibo.cn/profile/1792328230)
這個頁面是該博主的個人簡介頁面,直接拉到底,會有一個查看所有微博,點擊它會跳轉到該博主的所有微博頁面
這里打開開發者工具查看網絡請求,找到這個數據接口https://m.weibo.cn/api/container/getIndex?containerid=2304131792328230_-_WEIBO_SECOND_PROFILE_WEIBO,你會發現本頁面所有內容都在該請求返回的json數據包中。
接着往下滑頁面繼續觀察該請求窗口,就會發現這個接口的參數的規律。發現規律后就是老一套的模擬ajax加載獲取多頁數據,然后爬取目標內容。該數據接口參數如下:
https://m.weibo.cn/api/container/getIndex?containerid=?&page=? 其中參數id是指定該用戶,page就是頁數(默認從1開始).
(json數據可自行觀察規律,很容易找到要爬的數據所在)
2.開始寫代碼
創建一個WbGrawler類,並在構造方法初始化固定參數,如下:
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)
3.程序的優化
一開始試着爬了一頁爬了好久(可能是電腦性能或者網絡的問題),所以想着能不能開啟多線程爬取,但是用戶輸入的頁數是未知的,開啟多少個子線程貌似很難取舍,這里我想到了在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()
4.寫在最后
最后就沒了,源碼:https://github.com/zengtao614/WbCrawler ,上文如有錯誤或者疏漏之處歡迎指出,萬分感謝。