本文章精選了五個(gè)爬蟲實(shí)例,希望能夠給想要入門 Python 爬蟲的小伙伴兒們一些幫助。首先來看一個(gè)網(wǎng)易精選網(wǎng)站的爬蟲例子,可以爬取評價(jià)的商品很多,這里選擇“iPhone”關(guān)鍵字為例子,因?yàn)榇祟惿唐贩N類、樣式、顏色等比較多,利于后面的數(shù)據(jù)分析。進(jìn)入到網(wǎng)易精選官網(wǎng),搜索“iPhone”后,先隨便點(diǎn)進(jìn)一個(gè)商品。  在商品頁面,打開 Chrome 的控制臺,切換至 Network 頁,再把商品頁面切換到評價(jià)標(biāo)簽下,選擇一個(gè)評論文字,如“手機(jī)套很薄,裸機(jī)的手感”,在 Network 中搜索。 可以發(fā)現(xiàn),評論文字是通過 listByItemByTag.json 傳遞過來的,點(diǎn)擊進(jìn)入該請求,并拷貝出該請求的 URL: 將該 URL 放入 Postman 中,逐個(gè)嘗試 url query params,最后能夠發(fā)現(xiàn),只需保留 itemId 和 page 兩個(gè)請求參數(shù)即可。 請求返回的是一個(gè) JSON 格式的數(shù)據(jù),下面就是分析該 JSON 數(shù)據(jù)了。不難發(fā)現(xiàn),所有的評論數(shù)據(jù)都存儲在 commentList 中,我們只需保存該數(shù)據(jù)即可。下面就是如何獲取 itemId 的信息了,這個(gè)是產(chǎn)品的 ID,我們回到網(wǎng)易精選首頁,繼續(xù)分析。當(dāng)我們在搜索框中輸入關(guān)鍵字進(jìn)行搜索的時(shí)候,同樣能夠發(fā)現(xiàn)在 Network 中有很多請求,此時(shí)可以觀察各個(gè)請求,通過請求文件的名稱(此處需要一些經(jīng)驗(yàn),守規(guī)矩的程序員都不會亂起名字),我們可以定位到搜索時(shí)展示搜索結(jié)果的請求。 搜索一般都是 search,所以我們就鎖定了這個(gè) search.json 的請求。同樣把請求 URL 拷貝到 Postman 中,逐個(gè)驗(yàn)證傳參,最后保留 page 和 keyword 兩個(gè)參數(shù)即可。 該請求返回的數(shù)據(jù)較多,還是需要耐心的分析數(shù)據(jù),也能夠發(fā)現(xiàn),在 result->data->directly->searcherResult->result 下面的 id 值,即為我們要獲取的產(chǎn)品 ID。以上,我們基本完成了前期的分析工作,下面開始代碼的編寫。Python def search_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_id except: raise
我這里是獲取了 page 為 1 的產(chǎn)品 ID,下面就是通過產(chǎn)品 ID 來獲取不同產(chǎn)品下的評論信息。 通過前面的分析,我們可以知道,評論信息都是如下形式的,對這種形式的信息,我們可以很方便地存儲進(jìn)入 MongoDB,然后再慢慢分析數(shù)據(jù)里的內(nèi)容。{ 'skuInfo': [ '機(jī)型:iphone X', '顏色:透明白' ], 'frontUserName': 'c****x', 'frontUserAvatar': 'https://yanxuan.nosdn./c230f2c2a6e7223810755518ce5cd62f', 'content': '手機(jī)套很薄,裸機(jī)的手感,硅膠的摸著好舒服,看著屏幕都變大了。顏值很高。', 'createTime': 1554982110981, 'picList': [ 'https://yanxuan.nosdn./749cfcb4877be8e7fce449900d763731.jpg', 'https://yanxuan.nosdn./6744d04efcd7509556b710bc0d9fa6b0.jpg' ], 'commentReplyVO': null, 'memberLevel': 4, 'appendCommentVO': null, 'star': 5, 'itemId': 3444035 } 對于 MongoDB,我們既可以自己搭建,也可以使用網(wǎng)上免費(fèi)的服務(wù)。在這里我介紹一個(gè)免費(fèi)的 MongoDB 服務(wù)網(wǎng)站:mlab,使用很簡單,就不過多介紹使用過程了。數(shù)據(jù)庫有了,下面就是把數(shù)據(jù)保存進(jìn)去了。def details(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() if not res['data']['commentList']: break print('爬取第 %s 頁評論' % i) commentList = res['data']['commentList'] C_list.append(commentList) time.sleep(1) # save to mongoDB try: mongo_collection.insert_many(commentList) except: continue return C_list except: raise 在這里,details 這個(gè)函數(shù)還返回了評論的列表,如果你還有其他的邏輯需要對評論處理,可以接收這個(gè)函數(shù)的返回值,然后處理該列表。最后爬取完成之后,總共是七千多條數(shù)據(jù),下面就可以根據(jù)個(gè)人需要做一些分析了。 完整代碼地址:https://github.com/zhouwei713/dataanalysis/tree/master/you163spider。爬取的數(shù)據(jù) MongoDB 鏈接:conn = MongoClient('mongodb://%s:%s@ds149974.mlab.com:49974/you163' % ('you163', 'you163')) db = conn.you163 mongo_collection = db.iPhone

Boss 直聘網(wǎng)站爬取 其實(shí)分析的步驟都是類似的,無外乎是先分析網(wǎng)站信息,頁面組成,然后編寫代碼爬取數(shù)據(jù),最后再保存數(shù)據(jù)。我們選取一個(gè)崗位來分析,比如:Python。在 Boss 直聘的官網(wǎng)上搜索 Python,可以看到瀏覽器的 URL 變?yōu)槿缦拢?/span> 把該地址復(fù)制到 Postman 嘗試訪問,發(fā)現(xiàn)無法得到正確的返回: 此時(shí),再次回到瀏覽器,查看該請求下面的 headers,可以看到其中有一個(gè) cookie,是很長的一串字符串,我們拷貝這個(gè) cookie 到 Postman 中,再次請求: 成功了,看來 Boss 直聘網(wǎng)也只是做了簡單的 cookies 驗(yàn)證。下面就是解析 HTML 數(shù)據(jù)了,我比較習(xí)慣用 BeautifulSoup 這個(gè)庫來解析。from bs4 import BeautifulSoup import requests
url = 'https://www.zhipin.com/job_detail/?query=python&city=101010100'
res = requests.get(url, headers=header).text print(res) content = BeautifulSoup(res, 'html.parser') ul = content.find_all('ul') print(ul[12]) 可以使用 BeautifulSoup 的 find 函數(shù)來查找 HTML 的元素標(biāo)簽,個(gè)人覺得還是挺方便的。我們通過分析 HTML 網(wǎng)頁可以知道,所有的工作信息都是保存在 ul 這個(gè)標(biāo)簽中的,我們可以通過上面的代碼拿到頁面中所有的 ul 標(biāo)簽,find_all 返回的是一個(gè)列表,然后再查看,工作具體位于第幾個(gè) ul 中,這樣就拿到具體的工作信息了。 如圖中所示,我們需要抓取紅框中的信息,主要分為四部分。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 的詳情信息,需要用到正則表達(dá)式來分割字符串,我們先來看下該部分的原始形式: 又可以把該部分切分成三塊,site、year 和 edu??梢允褂谜齽t的 group 特性,幫助我們完成切分,最后我寫的正則如下:rege = r'<p>([\u4e00-\u9fa5 ]+)<em class='vline'></em>([\d+-年]+|[\u4e00-\u9fa5]+)<em class='vline'></em>([\u4e00-\u9fa5]+)'
正則表達(dá)式的具體寫法這里就不說了,不熟悉的可以自行查找下。
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) 由于我們最后還是得到了一個(gè)列表,里面包含字典,同樣可以快捷的保存到 MongoDB 中。通過查看 Boss 網(wǎng)站的下一頁源碼可得到翻頁 URL 的規(guī)律:https://www.zhipin.com/c101010100/?query=python&page= def jobs(page):
for i in range(1, page + 1): job_list = [] try: print('正在抓取第 %s 頁數(shù)據(jù)' % 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 mongoDB try: mongo_collection.insert_many(job_list) except: continue time.sleep(1) except: continue 因?yàn)槲疑厦娴恼诒磉_(dá)式并不能匹配所有的情況,所以使用 try...except 來忽略了其他不規(guī)則的情況。job 詳情抓取完畢之后,開始抓取崗位詳情,就是每個(gè) job 的具體要求,畢竟知己知彼,百戰(zhàn)不殆。我們可以從 URI 中獲得每個(gè)工作的詳情頁面地址,然后再拼接到 Boss 的主 URL 上:https://www.zhipin.com/job_detail/a8920821a7487a901HJ43tm7EFY~.html 再來看下工作詳情頁面,所有的任職描述都在如下的 div 標(biāo)簽中:沒有什么特殊的,直接用 BeautifulSoup 解析即可。job_conn = MongoClient('mongodb://%s:%s@ds151612.mlab.com:51612/boss' % ('boss', 'boss123')) job_db = job_conn.boss job_collection = job_db.boss
details_collection = job_db.job_details
def run_main(): jobs = job_collection.find() for job in jobs: print('獲得工作的uri ', job['uri']) get_details(job) time.sleep(1)
def get_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: raise
if __name__ == '__main__': run_main() 注意下這里的 MongoDB 配置,是同一個(gè) db 下的不同 collection。這樣,也就完成了直聘網(wǎng)站相關(guān)崗位的數(shù)據(jù)爬取。完整代碼地址:https://github.com/zhouwei713/dataanalysis/tree/master/bossspider。爬取的數(shù)據(jù) MongoDB 鏈接:job_conn = MongoClient('mongodb://%s:%s@ds151612.mlab.com:51612/boss' % ('boss', 'boss123')) job_db = job_conn.boss job_collection = job_db.boss details_collection = job_db.job_details
這里的微博爬蟲,我主要實(shí)現(xiàn)的是輸入你關(guān)心的某個(gè)大 V 的微博名稱,以及某條微博的相關(guān)內(nèi)容片段,即可自動爬取相關(guān)該大 V 一段時(shí)間內(nèi)發(fā)布的微博信息和對應(yīng)微博的評論信息。與上面的 Boss 直聘網(wǎng)站類似,爬取微博也需要獲取響應(yīng)的 cookie。 用瀏覽器打開微博頁面,拷貝出對應(yīng)的 Cookie,保存到本地。既然是某位大 V,這里就肯定涉及到了搜索的事情,我們可以先來嘗試下微博自帶的搜索,地址如下:同樣是先放到 Postman 里請求下,看看能不能直接訪問: 是可以的,這就省去了我們很多的麻煩。下面就是來分析并解析響應(yīng)消息,拿到對我們有用的數(shù)據(jù)。經(jīng)過觀察可知,這個(gè)接口返回的數(shù)據(jù)中,有一個(gè) UID 信息,是每個(gè)微博用戶的唯一 ID,我們可以拿過來留作后面使用。 至于要如何定位到這個(gè) UID,我也已經(jīng)在圖中做了標(biāo)注,相信你只要簡單分析下就能明白。def get_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 == '微博個(gè)人認(rèn)證': uid = href_list[2].get('uid') return uid elif title == '微博會員': uid = href_list[2].get('uid') return uid else: print('There are something wrong') return False except: raise 還是通過 BeautifulSoup 來定位獲取元素,最后返回 UID 信息。M 站一般是指手機(jī)網(wǎng)頁端的頁面,也就是為了適配 mobile 移動端而制作的頁面。一般的網(wǎng)站都是在原網(wǎng)址前面加“m.”來作為自己 M 站的地址,比如:m.baidu.com 就是百度的 M 站。我們來打開微博的 M 站,再進(jìn)入到林志玲的微博頁面看看 Network 中的請求,有沒有什么驚喜呢?我們首先發(fā)現(xiàn)了這樣一個(gè) 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 接著繼續(xù)拖動網(wǎng)頁,發(fā)現(xiàn) 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 類似,但是第一個(gè)返回的數(shù)據(jù)是用戶信息,而第二個(gè)返回的則是用戶的微博信息,顯然第二個(gè) URL 是我們需要的。同樣道理,把第二個(gè) URL 放到 Postman 中,看看哪些參數(shù)是可以省略的。 最后我們發(fā)現(xiàn),只要傳入正確的 containerid 信息,就能夠返回對應(yīng)的微博信息,可是 containerid 信息又從哪里來呢?我們剛剛獲得了一個(gè) UID 信息,現(xiàn)在來嘗試下能不能通過這個(gè) UID 來獲取到 containerid 信息。這里就又需要一些經(jīng)驗(yàn)了,我可以不停的嘗試給接口“m.weibo.cn/api/container/getIndex”添加不同的參數(shù),看看它會返回些什么信息,比如常見的參數(shù)名稱 type、id、value、name 等。最終,在我不懈的努力下,發(fā)現(xiàn) type 和 value 的組合是成功的,可以拿到對應(yīng)的 containerid 信息。 這個(gè)地方真的不有任何捷徑了,只能靠嘗試和經(jīng)驗(yàn)。現(xiàn)在就可以編寫代碼,獲取對應(yīng)的 containerid 了(如果你細(xì)心的話,還可以看到這個(gè)接口還返回了很多有意思的信息,可以自己嘗試著抓?。?。def get_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 userinfo except: raise 拿到 containerid 信息之后,我們就可以使用上面第二個(gè) URL 來獲取微博信息了,這里還是同樣的問題——分頁。怎么處理分頁呢,繼續(xù)改造這個(gè) getIndex 接口,繼續(xù)嘗試傳遞不同的參數(shù)給它。這次給它傳遞 containerid 和 page 信息,就可以完成分頁請求了。 傳遞的 page 為 3 時(shí),其實(shí)是獲取當(dāng)前新浪微博的第 4 頁數(shù)據(jù),后面我們就可以用這個(gè) URL 來獲取微博信息了。該接口返回的是 JSON 數(shù)據(jù),解析起來就比較方便了。微博信息就保存在 res['data']['cards'] 下面,有評論、轉(zhuǎn)發(fā)、點(diǎn)贊數(shù)量等信息。于是我們解析該 JSON 數(shù)據(jù)的函數(shù)就有了:def get_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'] # 轉(zhuǎn)發(fā)數(shù)量 comments_count = mblog['comments_count'] # 評論數(shù)量 attitudes_count = mblog['attitudes_count'] # 點(diǎn)贊數(shù)量 with open(name, 'a', encoding='utf-8') as f: f.write('----第' + str(page) + '頁,第' + str(i + 1) + '條微博----' + '\n') f.write('微博地址:' + str(scheme) + '\n' + '發(fā)布時(shí)間:' + str(create_time) + '\n' + '微博內(nèi)容:' + mblog_text + '\n' + '點(diǎn)贊數(shù):' + str(attitudes_count) + '\n' + '評論數(shù):' + str(comments_count) + '\n' + '轉(zhuǎn)發(fā)數(shù):' + str(reposts_count) + '\n') blog_dict['mblog_id'] = mblog_id blog_dict['mblog_text'] = mblog_text blog_dict['create_time'] = create_time return blog_dict else: print('沒有任何微博哦') return False
第一個(gè)參數(shù),接受的值為 res['data']['cards'] 的返回值,是一個(gè)字典類型數(shù)據(jù); 第二個(gè)參數(shù),是外層調(diào)用函數(shù)的循環(huán)計(jì)數(shù)器; 第三個(gè)參數(shù),是要爬取的大 V 名稱; 第四個(gè)參數(shù),是正在爬取的頁碼。 我們還要實(shí)現(xiàn)通過微博的一些字段,來定位到某個(gè)微博,從而抓取該微博下的評論的功能。再定義一個(gè)函數(shù),調(diào)用上面的 get_blog_info 函數(shù),從其返回的字典中拿到對應(yīng)的微博信息,再和需要比對的我們輸入的微博字段做比較,如果包含,那么就說明找到我們要的微博啦。def get_blog_by_text(containerid, blog_text, name): blog_list = [] page = 1 while True: try: url = 'https://m.weibo.cn/api/container/getIndex?containerid=%s&page=%s' % (containerid, page) res_code = requests.get(url).status_code if res_code == 418: print('訪問太頻繁,過會再試試吧') return False 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 is False: break mblog_text = blog_dict['mblog_text'] create_time = blog_dict['create_time'] if blog_text in mblog_text: print('找到相關(guān)微博') return blog_dict['mblog_id'] elif checkTime(create_time, config.day) is False: print('沒有找到相關(guān)微博') return blog_list page += 1 time.sleep(config.sleep_time) else: print('沒有任何微博哦') break
except: pass 這里調(diào)用了一個(gè)工具函數(shù) checkTime 和一個(gè)配置文件 config。def checkTime(inputtime, day): try: intime = datetime.datetime.strptime('2019-' + inputtime, '%Y-%m-%d') except: return '時(shí)間轉(zhuǎn)換失敗'
now = datetime.datetime.now() n_days = now - intime days = n_days.days if days < day: return True else: return False 定義這個(gè)函數(shù)的目的是為了限制搜索時(shí)間,比如對于 90 天以前的微博,就不再搜索了,也是提高效率。而 config 配置文件里,則定義了一個(gè)配置項(xiàng) day,來控制可以搜索的時(shí)間范圍:day = 90 # 最久抓取的微博時(shí)間,60即為只抓取兩個(gè)月前到現(xiàn)在的微博 sleep_time = 5 # 延遲時(shí)間,建議配置5-10s 我們進(jìn)入某一個(gè)微博頁面,進(jìn)入到評論區(qū):https://weibo.com/1312412824/HxFY84Gqb?filter=hot&root_comment_id=0&type=comment#_rnd1567155548217 
從 Network 中可以拿到一個(gè)請求 URL:https://weibo.com/aj/v6/comment/big?ajwvr=6&id=4380261561116383&from=singleWeiBo&__rnd=1567155729639 同樣使用 Postman 進(jìn)行 URL 精簡和分頁處理,可以得到最后的 URL 為:
https://weibo.com/aj/v6/comment/big?ajwvr=6&id=%s&page=%s def get_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 comment
def download_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') 最后,我們開始定義運(yùn)行函數(shù),把需要用戶輸入的相關(guān)信息都從運(yùn)行函數(shù)中獲取并傳遞給后面的邏輯函數(shù)中。from weibo_spider import WeiBo from config import headers
def main(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 = 100 while spider_type not in ('Text', 'text', 'Page', 'page'): spider_type = input('type spider type(Text or Page): ') ... 通過 input 函數(shù)接受用戶輸入信息,再判斷程序執(zhí)行。class WeiBo(object):
def __init__(self, name, headers): self.name = name self.headers = headers
def get_uid(self): # 獲取用戶的 UID ...
def get_userinfo(self, uid): # 獲取用戶信息,包括 containerid ...
def get_blog_by_page(self, containerid, page, name): # 獲取 page 頁的微博信息 ...
def get_blog_by_text(self, containerid, blog_text, name): # 一個(gè)簡單的搜索功能,根據(jù)輸入的內(nèi)容查找對應(yīng)的微博 ...
def get_comment(self, mblog_id, page): # 與上個(gè)函數(shù)配合使用,用于獲取某個(gè)微博的評論 ...
def download_comment(self, comment): # 下載評論 ... 在類的初始化函數(shù)中,傳入需要爬取的大 V 名稱和我們準(zhǔn)備好的 headers(cookie),然后把上面寫好的函數(shù)寫道該類下,后面該類的實(shí)例 weibo 就能夠調(diào)用這些函數(shù)了。import datetime from config import day
def checkTime(inputtime, day): ...
def get_blog_info(cards, i, name, page): ...
 完整代碼地址:https://github.com/zhouwei713/weibo_spider。繼續(xù)爬取懂球帝的女神大會數(shù)據(jù)。不過這里有一個(gè)問題,以前的懂球帝是帶有搜索功能的,所以我們能從搜索功能中找到一個(gè)用于搜索的 API,但是現(xiàn)在該功能不見了,所以這里已經(jīng)沒有辦法展示如何拿到搜索 API 的過程了。http://api.dongqiudi.com/search?keywords=&type=all&page= 我們可以通過給 keyword 傳入“女神大會”關(guān)鍵字,來獲取到女神大會相關(guān)的信息: 于是,我們可以通過輸入不同的 page 數(shù)值,獲取到所有的 JSON 信息,并解析 JSON,保存我們需要的數(shù)據(jù):def get_list(page): nvshen_id_list = [] nvshen_id_picture = [] for i in range(1, page): print('獲取第' + str(i) + '頁數(shù)據(jù)') 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 我們把上面 API 拿到的 url 字段放到懂球帝的主地址下,拼接成如下 url:http://www.dongqiudi.com/news/1193890 打開該 URL,發(fā)現(xiàn)確實(shí)是對應(yīng)的詳情頁面: 我們觀察該頁面,發(fā)現(xiàn)球迷朋友們的評分都是展示在頁面上的,且是上一期女神的評分,于是我決定把所有的 HTML 下載到本地,然后再慢慢解析頁面,抽取數(shù)據(jù):def download_page(nvshen_id_list): for i in nvshen_id_list: print('正在下載ID為' + i + '的HTML網(wǎng)頁') url = 'https://www.dongqiudi.com/news/%s' % i download = DownloadPage() html = download.getHtml(url) download.saveHtml(i, html) time.sleep(2)
class DownloadPage(object): def getHtml(self, url): html = requests.get(url=url, cookies=config.session, headers=config.header).content return html
def saveHtml(self, file_name, file_content): with open('html_page/' + file_name + '.html', 'wb') as f: f.write(file_content) download_page 函數(shù)接收一個(gè)列表(就是上一個(gè)函數(shù)的返回值 nvshen_id_list),通過 requests 庫來請求頁面并保存至本地。同時(shí)為了方式請求過于頻繁,做了 2 秒的延時(shí)等待設(shè)置。這里還要注意一點(diǎn)的是,這里是設(shè)置了 cookies 和 headers 的,否則是無法拿到正常 HTML 數(shù)據(jù)的,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 的解析,我依舊使用 BeautifulSoup。主要需要獲取 score 信息,但是由于好多 HTML 頁面寫的都不是很規(guī)則,故而這里耗費(fèi)了比較多的時(shí)間。
content.find_all('span', attrs={'style': 'color:#ff0000'})
但是有些頁面的規(guī)則不同,所以會存在無法解析一部分 HTML 文件,對于無法處理的文件,由于也不多,就手工處理了。 當(dāng)然,感覺這里使用正則應(yīng)該會好很多。代碼過長,為了不影響閱讀,就不貼出來了,可以到 GitHub 上查看完整代碼。def deal_loaclfile(nvshen_id_picture): files = os.listdir('html_page/') nvshen_list = [] special_page = [] for f in files: ... return nvshen_list, special_page
def get_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 代碼不是很規(guī)范,專門提出了這一部分: w_list = ['吳宣儀', '30萬', '826965', '68', '825847', 'https://img1.dongqiudi.com/fastdfs3/M00/74/54/180x135/crop/-/ChOxM1vIPpOAZT8AAAHza_WMyRk175.png'] g_list = ['關(guān)之琳', '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./fastdfs4/M00/CA/F7/ChMf8F0pTOKAaefqAA5nOMM0LK0171.jpg'] 對于數(shù)據(jù)的保存,直接保存在了本地的 CSV 中,代碼就不貼了。完整代碼地址:https://github.com/zhouwei713/data_analysis/tree/master/nvshendahui。展示網(wǎng)站地址:https://nvshen./。最后,我們再來看看豆瓣的爬取。其實(shí)豆瓣網(wǎng)站還是蠻友好的,沒有什么反爬機(jī)制,所以我們也需要投桃報(bào)李,注意爬蟲的速率,不要訪問過快。在豆瓣電影中搜索“王祖賢”,進(jìn)入王祖賢主頁后,點(diǎn)擊全部影人圖片,進(jìn)入到影人圖片頁面。在該頁面點(diǎn)擊下一頁,可以看到瀏覽器的 URL 變化如下:https://movie.douban.com/celebrity/1166896/photos/?type=C&start=30&sortby=like&size=a&subtype=a 繼續(xù)使用 Postman 來分析 URL,可以很輕松的得知,start 就是類似于 page 的頁數(shù)控制參數(shù),而且步長為 30,即第一頁是 start = 0,第二頁為 start = 30,第三頁為 start = 60,以此類推。
def get_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: break return comment_url_list, picture_list
之后,就可以下載海報(bào)了。 然后我們手動跳轉(zhuǎn)到每周海報(bào)的詳情頁面,繼續(xù)查看評論信息。 通過 BeautifulSoup 可以很容易地獲得評論信息,然后保存到 MongoDB 中。def get_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_list for 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。爬取的數(shù)據(jù) MongoDB 鏈接:client = pymongo.MongoClient('mongodb://douban:douban1989@ds149744.mlab.com:49744/douban') db = client.douban mongo_collection = db.comment
本文章分享了五個(gè)爬蟲的實(shí)戰(zhàn)例子,由于為了照顧篇幅,不至于過長,所以省去了一些繁瑣重復(fù)的代碼解釋。當(dāng)然,代碼部分難免會有些錯(cuò)誤、不規(guī)范的地方,還請包涵。在文章中,大部分代碼都是僅僅給出了一些思路和主要框架,還希望勵志于學(xué)習(xí)爬蟲的你,先好好思考爬蟲思路,手動自行敲一遍代碼,這樣才能達(dá)到最好的學(xué)習(xí)效果。作者:周蘿卜,Python 愛好者,個(gè)人公眾號:蘿卜大雜燴。
|