程式設計師如何煉成 Python 爬蟲「王者」?

2020-12-03 CSDN

作者 | 周蘿蔔

責編 | 郭芮

本文章精選了五個爬蟲實例,希望能夠給想要入門 Python 爬蟲的小夥伴兒們一些幫助。

網易精選評價爬取

首先來看一個網易精選網站的爬蟲例子,可以爬取評價的商品很多,這裡選擇「iPhone」關鍵字為例子,因為此類商品種類、樣式、顏色等比較多,利於後面的數據分析。

分析網頁

評論分析

進入到網易精選官網,搜索「iPhone」後,先隨便點進一個商品。

在商品頁面,打開 Chrome 的控制臺,切換至 Network 頁,再把商品頁面切換到評價標籤下,選擇一個評論文字,如「手機套很薄,裸機的手感」,在 Network 中搜索。

可以發現,評論文字是通過 listByItemByTag.json 傳遞過來的,點擊進入該請求,並拷貝出該請求的 URL:

將該 URL 放入 Postman 中,逐個嘗試 url query params,最後能夠發現,只需保留 itemId 和 page 兩個請求參數即可。

請求返回的是一個 JSON 格式的數據,下面就是分析該 JSON 數據了。

不難發現,所有的評論數據都存儲在 commentList 中,我們只需保存該數據即可。

下面就是如何獲取 itemId 的信息了,這個是產品的 ID,我們回到網易精選首頁,繼續分析。

產品 ID 獲取

當我們在搜索框中輸入關鍵字進行搜索的時候,同樣能夠發現在 Network 中有很多請求,此時可以觀察各個請求,通過請求文件的名稱(此處需要一些經驗,守規矩的程式設計師都不會亂起名字),我們可以定位到搜索時展示搜索結果的請求。

搜索一般都是 search,所以我們就鎖定了這個 search.json 的請求。同樣把請求 URL 拷貝到 Postman 中,逐個驗證傳參,最後保留 page 和 keyword 兩個參數即可。

該請求返回的數據較多,還是需要耐心的分析數據,也能夠發現,在 result->data->directly->searcherResult->result 下面的 id 值,即為我們要獲取的產品 ID。

以上,我們基本完成了前期的分析工作,下面開始代碼的編寫。

編寫代碼

獲取產品 ID

Pythondefsearch_keyword(keyword): uri = 'https://you.163.com/xhr/search/search.json' query = {"keyword": keyword,"page": 1 }try: res = requests.get(uri, params=query).json() result = res['data']['directly']['searcherResult']['result'] product_id = []for r in result: product_id.append(r['id'])return product_idexcept:raise

我這裡是獲取了 page 為 1 的產品 ID,下面就是通過產品 ID 來獲取不同產品下的評論信息。

通過前面的分析,我們可以知道,評論信息都是如下形式的,對這種形式的信息,我們可以很方便地存儲進入 MongoDB,然後再慢慢分析數據裡的內容。

{"skuInfo": ["機型:iphone X","顏色:透明白" ],"frontUserName": "c****x","frontUserAvatar": "https://yanxuan.nosdn.127.net/c230f2c2a6e7223810755518ce5cd62f","content": "手機套很薄,裸機的手感,矽膠的摸著好舒服,看著屏幕都變大了。顏值很高。","createTime": 1554982110981,"picList": ["https://yanxuan.nosdn.127.net/749cfcb4877be8e7fce449900d763731.jpg","https://yanxuan.nosdn.127.net/6744d04efcd7509556b710bc0d9fa6b0.jpg" ],"commentReplyVO": null,"memberLevel": 4,"appendCommentVO": null,"star": 5,"itemId": 3444035 }

對於 MongoDB,我們既可以自己搭建,也可以使用網上免費的服務。在這裡我介紹一個免費的 MongoDB 服務網站:mlab,使用很簡單,就不過多介紹使用過程了。

資料庫有了,下面就是把數據保存進去了。

defdetails(product_id): url = 'https://you.163.com/xhr/comment/listByItemByTag.json'try: C_list = []for i in range(1, 100): query = {"itemId": product_id,"page": i, } res = requests.get(url, params=query).json()ifnot res['data']['commentList']:break print("爬取第 %s 頁評論" % i) commentList = res['data']['commentList'] C_list.append(commentList) time.sleep(1)# save to mongoDBtry: mongo_collection.insert_many(commentList)except:continuereturn C_listexcept:raise

在這裡,details 這個函數還返回了評論的列表,如果你還有其他的邏輯需要對評論處理,可以接收這個函數的返回值,然後處理該列表。

最後爬取完成之後,總共是七千多條數據,下面就可以根據個人需要做一些分析了。

完整代碼地址:https://github.com/zhouwei713/dataanalysis/tree/master/you163spider。

爬取的數據 MongoDB 連結:

conn = MongoClient("mongodb://%s:%s@ds149974.mlab.com:49974/you163" % ('you163', 'you163'))db = conn.you163mongo_collection = db.iPhone

Boss 直聘網站爬取

其實分析的步驟都是類似的,無外乎是先分析網站信息,頁面組成,然後編寫代碼爬取數據,最後再保存數據。

頁面分析

我們選取一個崗位來分析,比如:Python。

在 Boss 直聘的官網上搜索 Python,可以看到瀏覽器的 URL 變為如下:

把該地址複製到 Postman 嘗試訪問,發現無法得到正確的返回:

此時,再次回到瀏覽器,查看該請求下面的 headers,可以看到其中有一個 cookie,是很長的一串字符串,我們拷貝這個 cookie 到 Postman 中,再次請求:

成功了,看來 Boss 直聘網也只是做了簡單的 cookies 驗證。

BeautifulSoup 使用

下面就是解析 HTML 數據了,我比較習慣用 BeautifulSoup 這個庫來解析。

from bs4 import BeautifulSoupimport requestsurl = 'https://www.zhipin.com/job_detail/?query=python&city=101010100'res = requests.get(url, headers=header).textprint(res)content = BeautifulSoup(res, "html.parser")ul = content.find_all('ul')print(ul[12])

可以使用 BeautifulSoup 的 find 函數來查找 HTML 的元素標籤,個人覺得還是挺方便的。

編寫代碼

我們通過分析 HTML 網頁可以知道,所有的工作信息都是保存在 ul 這個標籤中的,我們可以通過上面的代碼拿到頁面中所有的 ul 標籤,find_all 返回的是一個列表,然後再查看,工作具體位於第幾個 ul 中,這樣就拿到具體的工作信息了。

確定需要抓取的數據

如圖中所示,我們需要抓取紅框中的信息,主要分為四部分。

Python:可以得到該 job 具體頁面地址10-15K:每個 job 的薪資柯萊特集團:招聘公司名稱北京 朝陽區 望京|3-5年|學歷不限:該 job 的詳情信息對於前三個信息,還是比較好抓取的。

job_details_uri = job.find('h3', attrs={'class': 'name'}).find('a')['href']job_company = job.find('div', attrs={'class': 'company-text'}).find('h3', attrs={'class': 'name'}).find('a').textjob_salary = job.find('h3', attrs={'class': 'name'}).find('span', attrs={'class': 'red'}).text

對於 job 的詳情信息,需要用到正則表達式來分割字符串,我們先來看下該部分的原始形式:

又可以把該部分切分成三塊,site、year 和 edu。可以使用正則的 group 特性,幫助我們完成切分,最後我寫的正則如下:

rege = r'<p>([\u4e00-\u9fa5 ]+)<em></em>([\d+-年]+|[\u4e00-\u9fa5]+)<em></em>([\u4e00-\u9fa5]+)'

正則表達式的具體寫法這裡就不說了,不熟悉的可以自行查找下。

下面把這部分代碼整合到一起:

for job in jobs: job_dict = {} job_details_uri = job.find('h3', attrs={'class': 'name'}).find('a')['href'] job_company = job.find('div', attrs={'class': 'company-text'}).find('h3', attrs={'class': 'name'}).find('a').text job_salary = job.find('h3', attrs={'class': 'name'}).find('span', attrs={'class': 'red'}).text job_details = str(job.find('p')) print(job_details) job_rege = re.match(rege, job_details) job_dict['name'] = job_company job_dict['uri'] = job_details_uri job_dict['salary'] = job_salary job_dict['site'] = job_rege.group(1) job_dict['year'] = job_rege.group(2) job_dict['edu'] = job_rege.group(3) job_list.append(job_dict)print(job_list)

由於我們最後還是得到了一個列表,裡面包含字典,同樣可以快捷的保存到 MongoDB 中。

抓取多個頁面

通過查看 Boss 網站的下一頁源碼可得到翻頁 URL 的規律:

https://www.zhipin.com/c101010100/?query=python&page=

c101010100:是城市代碼,在我們這裡就代表北京query:也很明顯,就是我們的搜索關鍵字page:頁數

於是我們最後整合代碼如下:

defjobs(page):for i in range(1, page + 1): job_list = []try: print("正在抓取第 %s 頁數據" % i) uri = '/c101010100/?query=python&page=%s' % i res = requests.get(config.url + uri, headers=header).text content = BeautifulSoup(res, "html.parser") ul = content.find_all('ul') jobs = ul[12].find_all("li") ... print(job_list)# save to mongoDBtry: mongo_collection.insert_many(job_list)except:continue time.sleep(1)except:continue

因為我上面的正在表達式並不能匹配所有的情況,所以使用 try...except 來忽略了其他不規則的情況。

崗位詳情抓取

job 詳情抓取完畢之後,開始抓取崗位詳情,就是每個 job 的具體要求,畢竟知己知彼,百戰不殆。

我們可以從 URI 中獲得每個工作的詳情頁面地址,然後再拼接到 Boss 的主 URL 上:

https://www.zhipin.com/job_detail/a8920821a7487a901HJ43tm7EFY~.html

再來看下工作詳情頁面,所有的任職描述都在如下的 div 標籤中:

沒有什麼特殊的,直接用 BeautifulSoup 解析即可。

job_conn = MongoClient("mongodb://%s:%s@ds151612.mlab.com:51612/boss" % ('boss', 'boss123'))job_db = job_conn.bossjob_collection = job_db.bossdetails_collection = job_db.job_detailsdefrun_main(): jobs = job_collection.find()for job in jobs: print('獲得工作的uri ', job['uri']) get_details(job) time.sleep(1)defget_details(items): base_url = config.url url = base_url + items['uri'] company_name = items['name']try: res = requests.get(url, headers=header).text content = BeautifulSoup(res, "html.parser") text = content.find('div', attrs={'class': 'text'}).text.strip() result = {'name': company_name, 'details': text} details_collection.insert_one(result)except:raiseif __name__ == '__main__': run_main()

注意下這裡的 MongoDB 配置,是同一個 db 下的不同 collection。這樣,也就完成了直聘網站相關崗位的數據爬取。

完整代碼地址:https://github.com/zhouwei713/dataanalysis/tree/master/bossspider。

爬取的數據 MongoDB 連結:

job_conn = MongoClient("mongodb://%s:%s@ds151612.mlab.com:51612/boss" % ('boss', 'boss123'))job_db = job_conn.bossjob_collection = job_db.bossdetails_collection = job_db.job_details

微博爬取

這裡的微博爬蟲,我主要實現的是輸入你關心的某個大 V 的微博名稱,以及某條微博的相關內容片段,即可自動爬取相關該大 V 一段時間內發布的微博信息和對應微博的評論信息。

Cookie 獲取

與上面的 Boss 直聘網站類似,爬取微博也需要獲取響應的 cookie。

用瀏覽器打開微博頁面,拷貝出對應的 Cookie,保存到本地。

微博搜索

既然是某位大 V,這裡就肯定涉及到了搜索的事情,我們可以先來嘗試下微博自帶的搜索,地址如下:

s.weibo.com/user?q=林志玲

同樣是先放到 Postman 裡請求下,看看能不能直接訪問:

是可以的,這就省去了我們很多的麻煩。下面就是來分析並解析響應消息,拿到對我們有用的數據。

經過觀察可知,這個接口返回的數據中,有一個 UID 信息,是每個微博用戶的唯一 ID,我們可以拿過來留作後面使用。

至於要如何定位到這個 UID,我也已經在圖中做了標註,相信你只要簡單分析下就能明白。

defget_uid(name):try: url = 'https://s.weibo.com/user?q=%s' % name res = requests.get(url).text content = BeautifulSoup(res, 'html.parser') user = content.find('div', attrs={'class': 'card card-user-b s-pg16 s-brt1'}) user_info = user.find('div', attrs={'class': 'info'}).find('div') href_list = user_info.find_all('a')if len(href_list) == 3: title = href_list[1].get('title')if title == '微博個人認證': uid = href_list[2].get('uid')return uidelif title == '微博會員': uid = href_list[2].get('uid')return uidelse: print("There are something wrong")returnFalseexcept:raise

還是通過 BeautifulSoup 來定位獲取元素,最後返回 UID 信息。

M 站的利用

M 站一般是指手機網頁端的頁面,也就是為了適配 mobile 移動端而製作的頁面。一般的網站都是在原網址前面加「m.」來作為自己 M 站的地址,比如:m.baidu.com 就是百度的 M 站。

我們來打開微博的 M 站,再進入到林志玲的微博頁面看看 Network 中的請求,有沒有什麼驚喜呢?

我們首先發現了這樣一個 URL:

https://m.weibo.cn/api/container/getIndex?uid=1312412824&luicode=10000011&lfid=100103type%3D1%26q%3D%E6%9E%97%E5%BF%97%E7%8E%B2&containerid=1005051312412824

接著繼續拖動網頁,發現 Network 中又有類似的 URL:

https://m.weibo.cn/api/container/getIndex?uid=1312412824&luicode=10000011&lfid=100103type%3D1%26q%3D%E6%9E%97%E5%BF%97%E7%8E%B2&containerid=1076031312412824

URL 類似,但是第一個返回的數據是用戶信息,而第二個返回的則是用戶的微博信息,顯然第二個 URL 是我們需要的。同樣道理,把第二個 URL 放到 Postman 中,看看哪些參數是可以省略的。

最後我們發現,只要傳入正確的 containerid 信息,就能夠返回對應的微博信息,可是 containerid 信息又從哪裡來呢?我們剛剛獲得了一個 UID 信息,現在來嘗試下能不能通過這個 UID 來獲取到 containerid 信息。

這裡就又需要一些經驗了,我可以不停的嘗試給接口「m.weibo.cn/api/container/getIndex」添加不同的參數,看看它會返回些什麼信息,比如常見的參數名稱 type、id、value、name 等。最終,在我不懈的努力下,發現 type 和 value 的組合是成功的,可以拿到對應的 containerid 信息。

這個地方真的不有任何捷徑了,只能靠嘗試和經驗。

現在就可以編寫代碼,獲取對應的 containerid 了(如果你細心的話,還可以看到這個接口還返回了很多有意思的信息,可以自己嘗試著抓取)。

defget_userinfo(uid):try: url = 'https://m.weibo.cn/api/container/getIndex?type=uid&value=%s' % uid res = requests.get(url).json() containerid = res['data']['tabsInfo']['tabs'][1]['containerid'] mblog_counts = res['data']['userInfo']['statuses_count'] followers_count = res['data']['userInfo']['followers_count'] userinfo = {"containerid": containerid,"mblog_counts": mblog_counts,"followers_count": followers_count }return userinfoexcept:raise

代碼裡都是基本操作,不過多解釋了。

拿到 containerid 信息之後,我們就可以使用上面第二個 URL 來獲取微博信息了,這裡還是同樣的問題——分頁。怎麼處理分頁呢,繼續改造這個 getIndex 接口,繼續嘗試傳遞不同的參數給它。

這次給它傳遞 containerid 和 page 信息,就可以完成分頁請求了。

傳遞的 page 為 3 時,其實是獲取當前新浪微博的第 4 頁數據,後面我們就可以用這個 URL 來獲取微博信息了。

該接口返回的是 JSON 數據,解析起來就比較方便了。

微博信息就保存在 res['data']['cards'] 下面,有評論、轉發、點讚數量等信息。於是我們解析該 JSON 數據的函數就有了:

defget_blog_info(cards, i, name, page): blog_dict = {}if cards[i]['card_type'] == 9: scheme = cards[i]['scheme'] # 微博地址 mblog = cards[i]['mblog'] mblog_text = mblog['text'] create_time = mblog['created_at'] mblog_id = mblog['id'] reposts_count = mblog['reposts_count'] # 轉發數量 comments_count = mblog['comments_count'] # 評論數量 attitudes_count = mblog['attitudes_count'] # 點讚數量with open(name, 'a', encoding='utf-8') as f: f.write("----第" + str(page) + "頁,第" + str(i + 1) + "條微博----" + "\n") f.write("微博地址:" + str(scheme) + "\n" + "發布時間:" + str(create_time) + "\n" + "微博內容:" + mblog_text + "\n" + "點讚數:" + str(attitudes_count) + "\n" + "評論數:" + str(comments_count) + "\n" + "轉發數:" + str(reposts_count) + "\n") blog_dict['mblog_id'] = mblog_id blog_dict['mblog_text'] = mblog_text blog_dict['create_time'] = create_timereturn blog_dictelse: print("沒有任何微博哦")returnFalse

函數參數:

第一個參數,接受的值為 res['data']['cards'] 的返回值,是一個字典類型數據;第二個參數,是外層調用函數的循環計數器;第三個參數,是要爬取的大 V 名稱;第四個參數,是正在爬取的頁碼。最後函數返回一個字典。

搜索微博信息

我們還要實現通過微博的一些欄位,來定位到某個微博,從而抓取該微博下的評論的功能。

再定義一個函數,調用上面的 get_blog_info 函數,從其返回的字典中拿到對應的微博信息,再和需要比對的我們輸入的微博欄位做比較,如果包含,那麼就說明找到我們要的微博啦。

defget_blog_by_text(containerid, blog_text, name): blog_list = [] page = 1whileTrue:try: url = 'https://m.weibo.cn/api/container/getIndex?containerid=%s&page=%s' % (containerid, page) res_code = requests.get(url).status_codeif res_code == 418: print("訪問太頻繁,過會再試試吧")returnFalse res = requests.get(url).json() cards = res['data']['cards']if len(cards) > 0:for i in range(len(cards)): print("-----正在爬取第" + str(page) + "頁,第" + str(i+1) + "條微博------") blog_dict = get_blog_info(cards, i, name, page) blog_list.append(blog_dict)if blog_list isFalse:break mblog_text = blog_dict['mblog_text'] create_time = blog_dict['create_time']if blog_text in mblog_text: print("找到相關微博")return blog_dict['mblog_id']elif checkTime(create_time, config.day) isFalse: print("沒有找到相關微博")return blog_list page += 1 time.sleep(config.sleep_time)else: print("沒有任何微博哦")breakexcept:pass

這裡調用了一個工具函數 checkTime 和一個配置文件 config。

checkTime 函數定義如下:

defcheckTime(inputtime, day):try: intime = datetime.datetime.strptime("2019-" + inputtime, '%Y-%m-%d')except:return"時間轉換失敗" now = datetime.datetime.now() n_days = now - intime days = n_days.daysif days < day:returnTrueelse:returnFalse

定義這個函數的目的是為了限制搜索時間,比如對於 90 天以前的微博,就不再搜索了,也是提高效率。

而 config 配置文件裡,則定義了一個配置項 day,來控制可以搜索的時間範圍:

day = 90# 最久抓取的微博時間,60即為只抓取兩個月前到現在的微博sleep_time = 5# 延遲時間,建議配置5-10s

獲取評論信息

對於微博評論信息的獲取,要簡單很多。

我們進入某一個微博頁面,進入到評論區:

https://weibo.com/1312412824/HxFY84Gqb?filter=hot&root_comment_id=0&type=comment#_rnd1567155548217

從 Network 中可以拿到一個請求 URL:

https://weibo.com/aj/v6/comment/big?ajwvr=6&id=4380261561116383&from=singleWeiBo&__rnd=1567155729639

同樣使用 Postman 進行 URL 精簡和分頁處理,可以得到最後的 URL 為:

https://weibo.com/aj/v6/comment/big?ajwvr=6&id=%s&page=%s

id 就是要抓取評論的微博對應的 id,我們已經在上面的接口中拿到了;page 就是請求頁數。獲取評論及保存數據代碼:

defget_comment(self, mblog_id, page): comment = []for i in range(0, page): print("-----正在爬取第" + str(i) + "頁評論") url = 'https://weibo.com/aj/v6/comment/big?ajwvr=6&id=%s&page=%s' % (mblog_id, i) req = requests.get(url, headers=self.headers).text html = json.loads(req)['data']['html'] content = BeautifulSoup(html, "html.parser") comment_text = content.find_all('div', attrs={'class': 'WB_text'})for c in comment_text: _text = c.text.split(":")[1] comment.append(_text) time.sleep(config.sleep_time)return commentdefdownload_comment(self, comment): comment_pd = pd.DataFrame(columns=['comment'], data=comment) timestamp = str(int(time.time())) comment_pd.to_csv(timestamp + 'comment.csv', encoding='utf-8')

定義運行函數

最後,我們開始定義運行函數,把需要用戶輸入的相關信息都從運行函數中獲取並傳遞給後面的邏輯函數中。

from weibo_spider import WeiBofrom config import headersdefmain(name, spider_type, text, page, iscomment, comment_page): print("開始...") weibo = WeiBo(name, headers) ...if __name__ == '__main__': target_name = input("type the name: ") spider_type = input("type spider type(Text or Page): ") text = "你好" page_count = 10 iscomment = "No" comment_page_count = 100while spider_type notin ("Text", "text", "Page", "page"): spider_type = input("type spider type(Text or Page): ") ...

通過 input 函數接受用戶輸入信息,再判斷程序執行。

爬蟲類與工具集

最後再來看下程序中的 WeiBo 爬蟲類的定義:

classWeiBo(object):def__init__(self, name, headers): self.name = name self.headers = headersdefget_uid(self):# 獲取用戶的 UID ...defget_userinfo(self, uid):# 獲取用戶信息,包括 containerid ...defget_blog_by_page(self, containerid, page, name):# 獲取 page 頁的微博信息 ...defget_blog_by_text(self, containerid, blog_text, name):# 一個簡單的搜索功能,根據輸入的內容查找對應的微博 ...defget_comment(self, mblog_id, page):# 與上個函數配合使用,用於獲取某個微博的評論 ...defdownload_comment(self, comment):# 下載評論 ...

在類的初始化函數中,傳入需要爬取的大 V 名稱和我們準備好的 headers(cookie),然後把上面寫好的函數寫道該類下,後面該類的實例 weibo 就能夠調用這些函數了。

對於工具集,就是抽象出來的一些邏輯處理:

import datetimefrom config import daydefcheckTime(inputtime, day): ...defget_blog_info(cards, i, name, page): ...

最終程序運行示例:

完整代碼地址:https://github.com/zhouwei713/weibo_spider。

女神大會數據爬取

繼續爬取懂球帝的女神大會數據。

頁面 ID 信息獲取

不過這裡有一個問題,以前的懂球帝是帶有搜索功能的,所以我們能從搜索功能中找到一個用於搜索的 API,但是現在該功能不見了,所以這裡已經沒有辦法展示如何拿到搜索 API 的過程了。

但是搜索 API 我們還是可以使用的:

http://api.dongqiudi.com/search?keywords=&type=all&page=

我們可以通過給 keyword 傳入「女神大會」關鍵字,來獲取到女神大會相關的信息:

id 是對應的每個網頁的 id;thumb 是女神的封面圖片;url 對應的也是女神所在頁面的地址信息。於是,我們可以通過輸入不同的 page 數值,獲取到所有的 JSON 信息,並解析 JSON,保存我們需要的數據:

defget_list(page): nvshen_id_list = [] nvshen_id_picture = []for i in range(1, page): print("獲取第" + str(i) + "頁數據") url = 'http://api.dongqiudi.com/search?keywords=%E5%A5%B3%E7%A5%9E%E5%A4%A7%E4%BC%9A&type=all&page=' + str(i) html = requests.get(url=url).text news = json.loads(html)['news']if len(news) == 0: print("沒有更多啦")break nvshen_id = [k['id'] for k in news] nvshen_id_list = nvshen_id_list + nvshen_id nvshen_id_picture = nvshen_id_picture + [{k['id']: k['thumb']} for k in news] time.sleep(1)return nvshen_id_list, nvshen_id_picture

下載 HTML 頁面

我們把上面 API 拿到的 url 欄位放到懂球帝的主地址下,拼接成如下 url:

http://www.dongqiudi.com/news/1193890

打開該 URL,發現確實是對應的詳情頁面:

我們觀察該頁面,發現球迷朋友們的評分都是展示在頁面上的,且是上一期女神的評分,於是我決定把所有的 HTML 下載到本地,然後再慢慢解析頁面,抽取數據:

defdownload_page(nvshen_id_list):for i in nvshen_id_list: print("正在下載ID為" + i + "的HTML網頁") url = 'https://www.dongqiudi.com/news/%s' % i download = DownloadPage() html = download.getHtml(url) download.saveHtml(i, html) time.sleep(2)classDownloadPage(object):defgetHtml(self, url): html = requests.get(url=url, cookies=config.session, headers=config.header).contentreturn htmldefsaveHtml(self, file_name, file_content):with open('html_page/' + file_name + '.html', 'wb') as f: f.write(file_content)

download_page 函數接收一個列表(就是上一個函數的返回值 nvshen_id_list),通過 requests 庫來請求頁面並保存至本地。同時為了方式請求過於頻繁,做了 2 秒的延時等待設置。

這裡還要注意一點的是,這裡是設置了 cookies 和 headers 的,否則是無法拿到正常 HTML 數據的,headers 還是從瀏覽器中手動拷貝出來:

session = {'dqduid': 'yours'}header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win32; x32; rv:54.0) Gecko/20100101 Firefox/54.0','Connection': 'keep-alive'}

解析 HTML

對於 HTML 的解析,我依舊使用 BeautifulSoup。主要需要獲取 score 信息,但是由於好多 HTML 頁面寫的都不是很規則,故而這裡耗費了比較多的時間。

對於得分情況,我是這麼寫的:

content.find_all('span', attrs={'style': "color:#ff0000"})

但是有些頁面的規則不同,所以會存在無法解析一部分 HTML 文件,對於無法處理的文件,由於也不多,就手工處理了。

當然,感覺這裡使用正則應該會好很多。代碼過長,為了不影響閱讀,就不貼出來了,可以到 GitHub 上查看完整代碼。

defdeal_loaclfile(nvshen_id_picture): files = os.listdir('html_page/') nvshen_list = [] special_page = []for f in files: ...return nvshen_list, special_pagedefget_picture(c, t_list, n_id_p): nvshen_l = [] tmp_prev_id = c.find_all('a', attrs={"target": "_self"})for j in tmp_prev_id: ...

有一部分 HTML 代碼不是很規範,專門提出了這一部分:

w_list = ['吳宣儀', '30萬', '826965', '68', '825847','https://img1.dongqiudi.com/fastdfs3/M00/74/54/180x135/crop/-/ChOxM1vIPpOAZT8AAAHza_WMyRk175.png'] g_list = ['關之琳', '20萬', '813611', '88', '812559','https://img1.dongqiudi.com/fastdfs3/M00/6B/94/180x135/crop/-/ChOxM1u1gx2AZ7qmAABi3gRdHS8715.jpg'] t_list = ['佟麗婭', '22萬', '797779', '93', '795697','https://img1.dongqiudi.com/fastdfs3/M00/60/A7/180x135/crop/-/ChOxM1ufUh2AJdR0AABXtcU22fg956.jpg'] y_list = ['楊丞琳', '7萬', '1173681', '45', '1168209','https://img1.qunliao.info/fastdfs4/M00/CA/F7/ChMf8F0pTOKAaefqAA5nOMM0LK0171.jpg']

保存數據

對於數據的保存,直接保存在了本地的 CSV 中,代碼就不貼了。

完整代碼地址:https://github.com/zhouwei713/data_analysis/tree/master/nvshendahui。

展示網站地址:https://nvshen.luobodazahui.top/。

豆瓣海報爬取

最後,我們再來看看豆瓣的爬取。其實豆瓣網站還是蠻友好的,沒有什麼反爬機制,所以我們也需要投桃報李,注意爬蟲的速率,不要訪問過快。

我這裡就以女神王祖賢的海報來作為例子。

翻頁分析

在豆瓣電影中搜索「王祖賢」,進入王祖賢主頁後,點擊全部影人圖片,進入到影人圖片頁面。

在該頁面點擊下一頁,可以看到瀏覽器的 URL 變化如下:

https://movie.douban.com/celebrity/1166896/photos/?type=C&start=30&sortby=like&size=a&subtype=a

繼續使用 Postman 來分析 URL,可以很輕鬆的得知,start 就是類似於 page 的頁數控制參數,而且步長為 30,即第一頁是 start = 0,第二頁為 start = 30,第三頁為 start = 60,以此類推。

詳情頁分析

使用 Network 來查看頁面上的圖片信息:

這裡我們得到了兩個信息:

a 標籤中的連結可以得到每張圖片的評論信息;img 標籤中的連結可以用來保存女神的海報。對於這兩個信息 url,可以分別返回:

defget_posters(): comment_url_list = [] picture_list = []for i in range(0, 40000, 30): url = 'https://movie.douban.com/celebrity/1166896/photos/?type=C&start=%s&sortby=like&size=a&subtype=a' % str(i) req = requests.get(url).text content = BeautifulSoup(req, "html.parser") chekc_point = content.find('span', attrs={'class': 'next'}).find('a')if chekc_point != None: data = content.find_all('div', attrs={'class': 'cover'})for k in data: ulist = k.find('a')['href'] plist = k.find('img')['src'] comment_url_list.append(ulist) picture_list.append(plist)else:breakreturn comment_url_list, picture_list

之後,就可以下載海報了。

評論獲取

然後我們手動跳轉到每周海報的詳情頁面,繼續查看評論信息。

通過 BeautifulSoup 可以很容易地獲得評論信息,然後保存到 MongoDB 中。

defget_comment(comment_l): client = pymongo.MongoClient('mongodb://douban:douban1989@ds149744.mlab.com:49744/douban') db = client.douban mongo_collection = db.comment comment_list = [] comment = [] print("Save to MongoDB")for i in comment_l: response = requests.get(i).text content = BeautifulSoup(response, "html.parser") tmp_list = content.find_all('div', attrs={'class': 'comment-item'}) comment_list = comment_list + tmp_listfor k in comment_list: tmp_comment = k.find('p').text mongo_collection.insert_one({'comment': tmp_comment}) comment.append(tmp_comment) print("Save Finish!")

完整代碼地址:https://github.com/zhouwei713/douban/tree/master/wangzuxian_poster。

爬取的數據 MongoDB 連結:

client = pymongo.MongoClient('mongodb://douban:douban1989@ds149744.mlab.com:49744/douban')db = client.doubanmongo_collection = db.comment

總結

本文章分享了五個爬蟲的實戰例子,由於為了照顧篇幅,不至於過長,所以省去了一些繁瑣重複的代碼解釋。當然,代碼部分難免會有些錯誤、不規範的地方,還請包涵。

在文章中,大部分代碼都是僅僅給出了一些思路和主要框架,還希望勵志於學習爬蟲的你,先好好思考爬蟲思路,手動自行敲一遍代碼,這樣才能達到最好的學習效果。

聲明:本文為作者原創投稿,未經允許請勿轉載。

相關焦點

  • 初學者如何用「python爬蟲」技術抓取網頁數據?
    想要入門Python 爬蟲首先需要解決四個問題:1.熟悉python編程2.了解HTML3.了解網絡爬蟲的基本原理4.學習使用python爬蟲庫1、熟悉python編程剛開始入門爬蟲,初學者無需學習python的類,多線程,模塊和其他稍微困難的內容。
  • 開課吧Python:Python爬蟲是什麼?爬蟲工程師薪資怎麼樣?
    Python爬蟲是什麼?Python爬蟲是什麼?Python爬蟲是由Python程序開發的網絡爬蟲(webspider,webrobot),是按照一定規則自動抓取全球資訊網信息的程序或腳本。其實一般是通過程序在網頁上獲取你想要的數據,也就是自動抓取數據。為什麼需要用爬蟲?
  • python爬蟲-urllib、urllib2、requests三者關係
    只要人能看到的東西,理論上都是爬蟲可以獲取的。不論靜態頁面還是動態頁面。也不論pc端的頁面還是移動端的app。話有點大,但這就是本系列的目的。爬蟲編程,有很多語言可選,python、php、go、java···甚至是c。這裡我們選擇python作為爬蟲的程式語言,因為它簡單易上手,並且節約生命。
  • Python爬蟲開發的就業前景如何
    首先,在當前的大數據應用環境下,如果單純做Python爬蟲開發,那麼在崗位競爭力上還是有所欠缺的。要想找到一個比較滿意的工作崗位,還需要進一步完善自身的知識結構。隨著Python語言的發展,目前不少程式設計師開始轉向Python開發,在學習Python開發的過程中,一個比較常見的案例就是採用Python開發爬蟲。用Python開發爬蟲是比較方便的,尤其在當前的大數據時代,通過爬蟲來獲取Web數據是一個比較常見的數據採集方式,所以在大數據應用的早期,通過Python開發爬蟲是不少Python程式設計師的重要工作內容之一。
  • Python視頻教程網課編程零基礎入門數據分析網絡爬蟲全套Python...
    Mov 1-2在線編譯工具weblde使用之指南.mov 2-1如何在列表,字典,集合中根據條件.MOV 2-2 3 4命名 統計 字典.mov 2-5公共鍵.mov 2-6 如何讓字典保持有序.mov 2-7歷史記錄.mov 3-1 2迭代器.mov 3-3如何使用生成器函數實現迭代對象
  • Python開發簡單爬蟲【學習資料總結】
    一、簡單爬蟲架構 開發爬蟲步驟 python語言基礎:(帶你熟悉python語言的特性,學會使用python開發環境,使用python開發一些簡單的案例)
  • 如何快速學會Python爬蟲(入門篇)
    Python爬蟲入門二之爬蟲基礎了解3. Python爬蟲入門三之Urllib庫的基本使用4. Python爬蟲入門四之Urllib庫的高級用法5. Python爬蟲入門五之URLError異常處理6.
  • python開發爬蟲有門檻嗎?
    如果你不是科班出身,沒有開發經驗,初次接觸開發爬蟲這檔子事兒,相信這篇文章能幫到你。python開發爬蟲肯定是有門檻的。儘管python很簡單,上手不難,但是開發起來你會發現,開發爬蟲不只是單單會python就可以了,你還得需要下列這些技能。
  • Python,爬蟲開發的不二選擇
    如果我們把網際網路比作一張大的蜘蛛網,數據便是存放於蜘蛛網的各個節點,而爬蟲就是一隻小蜘蛛,沿著網絡抓取自己的獵物(數據)。爬蟲指的是:向網站發起請求,獲取資源後分析並提取有用數據的程序。如今,人類社會已經進入了大數據時代,如何高效的獲取數據,已經成了各個網際網路公司的技術研發重點,掌握爬蟲技術已經成為了大數據公司技術人員不可或缺的一項技能。
  • 「Python爬蟲與文本實例技術與應用」培訓班通知
    通過爬蟲獲取的海量信息,我們可以對其進行進一步的分析:市場預測、文本分析、機器學習方法等。     Python作為一門腳本語言,它靈活、易用、易學、適用場景多,實現程序快捷便利,早已經成為程式設計師們的一門編程利器。Python這門程式語言包羅萬象,可以說掌握了python,除了一些特殊環境和高度的性能要求,你可以用它做任何事。
  • Python爬蟲學到什麼程度就可以去找工作了?
    有朋友在群裡和大家討論,問的最多的問題就是,python 爬蟲學到什麼程度可以去找工作了,關於這點,和大家分享下我的理解。去招聘網上看看需求都有哪些,直接做個拉勾網爬蟲(有需要的私信)出結果了:仔細看看,我們可以得出以下幾點:1、 python 不是唯一可以做爬蟲的,很多語言都可以,
  • 如何開始寫你的第一個python腳本——簡單爬蟲入門!
    好多朋友在入門python的時候都是以爬蟲入手,而網絡爬蟲是近幾年比較流行的概念,特別是在大數據分析熱門起來以後,學習網絡爬蟲的人越來越多,哦對,現在叫數據挖掘了!其實,一般的爬蟲具有2個功能:取數據和存數據!好像說了句廢話。。。
  • Python破解反爬蟲:最新反爬蟲有道翻譯中英文互譯破解,附代碼
    由於爬蟲的出現,導致很多網頁都設置了反爬蟲機制:常見的反爬蟲機制就是在客戶端發出請求的時候,在請求的內容中新增一些內容,而這些內容都是經過「加密的」,每次請求都是不同的,這樣就導致了很多傳統的爬蟲失效。
  • Python 爬蟲面試題 170 道
    13.如何優雅的給一個函數加注釋?14.如何給變量加注釋?15.Python 代碼縮進中是否支持 Tab 鍵和空格混用。16.是否可以在一句 import 中導入多個庫?20.如何區別可變數據類型和不可變數據類型21.將"hello world"轉換為首字母大寫"Hello World"22.如何檢測字符串中只含有數字?
  • python爬蟲入門實戰!爬取博客文章標題和連結!
    最近有小夥伴和我留言想學python爬蟲,那麼就搞起來吧。準備階段爬蟲有什麼用呢?舉個最簡單的小例子,你需要《戰狼2》的所有豆瓣影評。最先想的做法可能是打開瀏覽器,進入該網站,找到評論,一個一個複製到文本中,保存,翻頁,接著複製,直到翻到最後一頁。
  • Python新手爬蟲,簡單製作抓取廖雪峰的教程的小爬蟲
    Python 數據處理 Python編程從入門到實踐 核心編程基礎教程 網絡爬蟲入門書籍 python視頻編程從入門到精通 程序設計教材人民郵電¥72.2領3元券話不多說,先來看看整個爬蟲過程吧!目標:廖雪峰官網&mdash;python教程工具:pycharm、python3.6、requests庫、time庫、lxml庫思路:85 抓取所有的目錄章節 url85 找到 url內教程主體所在的 div85 遍歷 div下所有標籤,文本和代碼部分內容直接保存85 區分其中的代碼、圖片、視頻 3個內容,
  • 人生苦短,我用Python,那麼問題來了,普通人要學python嗎?
    話說在金融和IT行當混跡了多年,python一直是被我隨手拿來當個小工具用用,有時候偷懶用python弄個excel自動化整理工具,有時候拿來上各種網站爬蟲搜集點信息,有時候也會拿來寫點量化小工具。那麼到底什麼是python?
  • Python 爬蟲面試題 170 道:2019 版
    .如何打亂一個列表的元素?90.寫一段匹配郵箱的正則表達式其他內容91.解釋一下 python 中 pass 語句的作用?92.簡述你對 input()函數的理解93.python 中的 is 和==94.Python 中的作用域95.三元運算寫法和應用場景?96.了解 enumerate 麼?
  • 從零開始寫Python爬蟲,四大工具你值得擁有!
    如果你正在學習編程,那麼「爬蟲」絕對是你不可忽視的。那麼,學習python爬蟲之前需要哪些準備?一顆熱愛學習,不屈不撓的心一臺有鍵盤的電腦(什麼系統都行。我用的os x,所以例子會以這個為準)html相關的一些知識。
  • Python爬蟲從入門到精通只需要三個月
    如何入門Python,為了能夠幫助大家更輕鬆的學好Python開發,Python爬數據,Python數據分析等相關理論知識,給大家共同分享自己一套Python學習生活資料,文章最後面的有附屬的相關資料,無論你是大牛還是小白,是想轉行還是想入行都可以來了解,一起不斷進步以及一起努力學習