自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

手把手教你用Scrapy爬蟲框架爬取食品論壇數(shù)據(jù)并存入數(shù)據(jù)庫

安全 應(yīng)用安全
大家好,我是杯酒先生,這是我第一次寫這種分享項(xiàng)目的文章,可能很水,很不全面,而且肯定存在說錯(cuò)的地方,希望大家可以評論里加以指點(diǎn),不勝感激!

[[397000]]

大家好,我是杯酒先生,這是我第一次寫這種分享項(xiàng)目的文章,可能很水,很不全面,而且肯定存在說錯(cuò)的地方,希望大家可以評論里加以指點(diǎn),不勝感激!

一、前言

網(wǎng)絡(luò)爬蟲(又稱為網(wǎng)頁蜘蛛,網(wǎng)絡(luò)機(jī)器人),是一種按照一定的規(guī)則,自動(dòng)地抓取萬維網(wǎng)信息的程序或者腳本。另外一些不常使用的名字還有螞蟻、自動(dòng)索引、模擬程序或者蠕蟲。------百度百科

說人話就是,爬蟲是用來海量規(guī)則化獲取數(shù)據(jù),然后進(jìn)行處理和運(yùn)用,在大數(shù)據(jù)、金融、機(jī)器學(xué)習(xí)等等方面都是必須的支撐條件之一。

目前在一線城市中,爬蟲的崗位薪資待遇都是比較客觀的,之后提升到中、高級爬蟲工程師,數(shù)據(jù)分析師、大數(shù)據(jù)開發(fā)崗位等,都是很好的過渡。

二、項(xiàng)目目標(biāo)

本此介紹的項(xiàng)目其實(shí)不用想的太過復(fù)雜,最終要實(shí)現(xiàn)的目標(biāo)也就是將帖子的每條評論爬取到數(shù)據(jù)庫中,并且做到可以更新數(shù)據(jù),防止重復(fù)爬取,反爬等措施。

三、項(xiàng)目準(zhǔn)備

這部分主要是介紹本文需要用到的工具,涉及的庫,網(wǎng)頁等信息等

軟件:PyCharm

需要的庫:Scrapy, selenium, pymongo, user_agent,datetime

目標(biāo)網(wǎng)站:

  1. http://bbs.foodmate.net 

插件:chromedriver(版本要對)

四、項(xiàng)目分析

1、確定爬取網(wǎng)站的結(jié)構(gòu)

簡而言之:確定網(wǎng)站的加載方式,怎樣才能正確的一級一級的進(jìn)入到帖子中抓取數(shù)據(jù),使用什么格式保存數(shù)據(jù)等。

其次,觀察網(wǎng)站的層級結(jié)構(gòu),也就是說,怎么根據(jù)板塊,一點(diǎn)點(diǎn)進(jìn)入到帖子頁面中,這對本次爬蟲任務(wù)非常重要,也是主要編寫代碼的部分。

2、如何選擇合適的方式爬取數(shù)據(jù)?

目前我知道的爬蟲方法大概有如下(不全,但是比較常用):

1)request框架:運(yùn)用這個(gè)http庫可以很靈活的爬取需要的數(shù)據(jù),簡單但是過程稍微繁瑣,并且可以配合抓包工具對數(shù)據(jù)進(jìn)行獲取。但是需要確定headers頭以及相應(yīng)的請求參數(shù),否則無法獲取數(shù)據(jù);很多app爬取、圖片視頻爬取隨爬隨停,比較輕量靈活,并且高并發(fā)與分布式部署也非常靈活,對于功能可以更好實(shí)現(xiàn)。

2)scrapy框架:scrapy框架可以說是爬蟲最常用,最好用的爬蟲框架了,優(yōu)點(diǎn)很多:scrapy 是異步的;采取可讀性更強(qiáng)的 xpath 代替正則;強(qiáng)大的統(tǒng)計(jì)和 log 系統(tǒng);同時(shí)在不同的 url 上爬行;支持 shell 方式,方便獨(dú)立調(diào)試;支持寫 middleware方便寫一些統(tǒng)一的過濾器;可以通過管道的方式存入數(shù)據(jù)庫等等。這也是本次文章所要介紹的框架(結(jié)合selenium庫)。

五、項(xiàng)目實(shí)現(xiàn)

1、第一步:確定網(wǎng)站類型

首先解釋一下是什么意思,看什么網(wǎng)站,首先要看網(wǎng)站的加載方式,是靜態(tài)加載,還是動(dòng)態(tài)加載(js加載),還是別的方式;根據(jù)不一樣的加載方式需要不同的辦法應(yīng)對。然后我們觀察今天爬取的網(wǎng)站,發(fā)現(xiàn)這是一個(gè)有年代感的論壇,首先猜測是靜態(tài)加載的網(wǎng)站;我們開啟組織 js 加載的插件,如下圖所示。

刷新之后發(fā)現(xiàn)確實(shí)是靜態(tài)網(wǎng)站(如果可以正常加載基本都是靜態(tài)加載的)。

2、第二步:確定層級關(guān)系

其次,我們今天要爬取的網(wǎng)站是食品論壇網(wǎng)站,是靜態(tài)加載的網(wǎng)站,在之前分析的時(shí)候已經(jīng)了解了,然后是層級結(jié)構(gòu):

大概是上面的流程,總共有三級遞進(jìn)訪問,之后到達(dá)帖子頁面,如下圖所示。

部分代碼展示:

一級界面:

  1. def parse(self, response): 
  2.     self.logger.info("已進(jìn)入網(wǎng)頁!"
  3.     self.logger.info("正在獲取版塊列表!"
  4.     column_path_list = response.css('#ct > div.mn > div:nth-child(2) > div')[:-1] 
  5.     for column_path in column_path_list: 
  6.         col_paths = column_path.css('div > table > tbody > tr > td > div > a').xpath('@href').extract() 
  7.         for path in col_paths: 
  8.             block_url = response.urljoin(path) 
  9.             yield scrapy.Request( 
  10.                 url=block_url, 
  11.                 callback=self.get_next_path, 
  12.             ) 

二級界面:

  1. def get_next_path(self, response): 
  2.     self.logger.info("已進(jìn)入版塊!"
  3.     self.logger.info("正在獲取文章列表!"
  4.     if response.url == 'http://www.foodmate.net/know/'
  5.         pass 
  6.     else
  7.         try: 
  8.             nums = response.css('#fd_page_bottom > div > label > span::text').extract_first().split(' ')[-2] 
  9.         except
  10.             nums = 1 
  11.         for num in range(1, int(nums) + 1): 
  12.             tbody_list = response.css('#threadlisttableid > tbody'
  13.             for tbody in tbody_list: 
  14.                 if 'normalthread' in str(tbody): 
  15.                     item = LunTanItem() 
  16.                     item['article_url'] = response.urljoin( 
  17.                         tbody.css('* > tr > th > a.s.xst').xpath('@href').extract_first()) 
  18.                     item['type'] = response.css( 
  19.                         '#ct > div > div.bm.bml.pbn > div.bm_h.cl > h1 > a::text').extract_first() 
  20.                     item['title'] = tbody.css('* > tr > th > a.s.xst::text').extract_first() 
  21.                     item['spider_type'] = "論壇" 
  22.                     item['source'] = "食品論壇" 
  23.                     if item['article_url'] != 'http://bbs.foodmate.net/'
  24.                         yield scrapy.Request( 
  25.                             url=item['article_url'], 
  26.                             callback=self.get_data, 
  27.                             meta={'item': item, 'content_info': []} 
  28.                         ) 
  29.         try: 
  30.             callback_url = response.css('#fd_page_bottom > div > a.nxt').xpath('@href').extract_first() 
  31.             callback_url = response.urljoin(callback_url) 
  32.             yield scrapy.Request( 
  33.                 url=callback_url, 
  34.                 callback=self.get_next_path, 
  35.             ) 
  36.         except IndexError: 
  37.             pass 

三級界面:

  1. def get_data(self, response): 
  2.     self.logger.info("正在爬取論壇數(shù)據(jù)!"
  3.     item = response.meta['item'
  4.     content_list = [] 
  5.     divs = response.xpath('//*[@id="postlist"]/div'
  6.     user_name = response.css('div > div.pi > div:nth-child(1) > a::text').extract() 
  7.     publish_time = response.css('div.authi > em::text').extract() 
  8.     floor = divs.css('* strong> a> em::text').extract() 
  9.     s_id = divs.xpath('@id').extract() 
  10.     for i in range(len(divs) - 1): 
  11.         content = '' 
  12.         try: 
  13.  
  14.             strong = response.css('#postmessage_' + s_id[i].split('_')[-1] + '').xpath('string(.)').extract() 
  15.             for s in strong: 
  16.                 content += s.split(';')[-1].lstrip('\r\n'
  17.             datas = dict(content=content,  # 內(nèi)容 
  18.                          reply_id=0,  # 回復(fù)的樓層,默認(rèn)0 
  19.                          user_name=user_name[i],  # ⽤戶名 
  20.                          publish_time=publish_time[i].split('于 ')[-1],  # %Y-%m-%d %H:%M:%S' 
  21.                          id='#' + floor[i],  # 樓層 
  22.                          ) 
  23.             content_list.append(datas) 
  24.         except IndexError: 
  25.             pass 
  26.     item['content_info'] = response.meta['content_info'
  27.     item['scrawl_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S'
  28.     item['content_info'] += content_list 
  29.  
  30.     data_url = response.css('#ct > div.pgbtn > a').xpath('@href').extract_first() 
  31.     if data_url != None: 
  32.         data_url = response.urljoin(data_url) 
  33.         yield scrapy.Request( 
  34.             url=data_url, 
  35.             callback=self.get_data, 
  36.             meta={'item': item, 'content_info': item['content_info']} 
  37.         ) 
  38.     else
  39.         item['scrawl_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S'
  40.         self.logger.info("正在存儲(chǔ)!"
  41.         print('儲(chǔ)存成功'
  42.         yield item 

3、第三步:確定爬取方法

由于是靜態(tài)網(wǎng)頁,首先決定采用的是scrapy框架直接獲取數(shù)據(jù),并且通過前期測試發(fā)現(xiàn)方法確實(shí)可行,不過當(dāng)時(shí)年少輕狂,小看了網(wǎng)站的保護(hù)措施,由于耐心有限,沒有加上定時(shí)器限制爬取速度,導(dǎo)致我被網(wǎng)站加了限制,并且網(wǎng)站由靜態(tài)加載網(wǎng)頁變?yōu)椋簞?dòng)態(tài)加載網(wǎng)頁驗(yàn)證算法之后再進(jìn)入到該網(wǎng)頁,直接訪問會(huì)被后臺(tái)拒絕。

但是這種問題怎么會(huì)難道我這小聰明,經(jīng)過我短暫地思考(1天),我將方案改為scrapy框架 + selenium庫的方法,通過調(diào)用chromedriver,模擬訪問網(wǎng)站,等網(wǎng)站加載完了再爬取不就完了,后續(xù)證明這個(gè)方法確實(shí)可行,并且效率也不錯(cuò)。

實(shí)現(xiàn)部分代碼如下:

  1. def process_request(self, request, spider): 
  2.     chrome_options = Options() 
  3.     chrome_options.add_argument('--headless')  # 使用無頭谷歌瀏覽器模式 
  4.     chrome_options.add_argument('--disable-gpu'
  5.     chrome_options.add_argument('--no-sandbox'
  6.     # 指定谷歌瀏覽器路徑 
  7.     self.driver = webdriver.Chrome(chrome_options=chrome_options, 
  8.                                    executable_path='E:/pycharm/workspace/爬蟲/scrapy/chromedriver'
  9.     if request.url != 'http://bbs.foodmate.net/'
  10.         self.driver.get(request.url) 
  11.         html = self.driver.page_source 
  12.         time.sleep(1) 
  13.         self.driver.quit() 
  14.         return scrapy.http.HtmlResponse(url=request.url, body=html.encode('utf-8'), encoding='utf-8'
  15.                                         request=request) 

4、第四步:確定爬取數(shù)據(jù)的儲(chǔ)存格式

這部分不用多說,根據(jù)自己需求,將需要爬取的數(shù)據(jù)格式設(shè)置在items.py中。在工程中引用該格式保存即可:

  1. class LunTanItem(scrapy.Item): 
  2.     ""
  3.         論壇字段 
  4.     ""
  5.     title = Field()  # str: 字符類型 | 論壇標(biāo)題 
  6.     content_info = Field()  # str: list類型 | 類型list: [LunTanContentInfoItem1, LunTanContentInfoItem2] 
  7.     article_url = Field()  # str: url | 文章鏈接 
  8.     scrawl_time = Field()  # str: 時(shí)間格式 參照如下格式 2019-08-01 10:20:00 | 數(shù)據(jù)爬取時(shí)間 
  9.     source = Field()  # str: 字符類型 | 論壇名稱 eg: 未名BBS, 水木社區(qū), 天涯論壇 
  10.     type = Field()  # str: 字符類型 | 板塊類型 eg: '財(cái)經(jīng)''體育''社會(huì)' 
  11.     spider_type = Field()  # str: forum | 只能寫 'forum' 

5、第五步:確定保存數(shù)據(jù)庫

本次項(xiàng)目選擇保存的數(shù)據(jù)庫為mongodb,由于是非關(guān)系型數(shù)據(jù)庫,優(yōu)點(diǎn)顯而易見,對格式要求沒有那么高,可以靈活儲(chǔ)存多維數(shù)據(jù),一般是爬蟲優(yōu)選數(shù)據(jù)庫(不要和我說redis,會(huì)了我也用,主要是不會(huì))

代碼:

  1. import pymongo 
  2.  
  3. class FMPipeline(): 
  4.     def __init__(self): 
  5.         super(FMPipeline, self).__init__() 
  6.         # client = pymongo.MongoClient('139.217.92.75'
  7.         client = pymongo.MongoClient('localhost'
  8.         db = client.scrapy_FM 
  9.         self.collection = db.FM 
  10.  
  11.     def process_item(self, item, spider): 
  12.         query = { 
  13.             'article_url': item['article_url'
  14.         } 
  15.         self.collection.update_one(query, {"$set": dict(item)}, upsert=True
  16.         return item 

這時(shí),有聰明的盆友就會(huì)問:如果運(yùn)行兩次爬取到了一樣的數(shù)據(jù)怎么辦呢?(換句話說就是查重功能)

這個(gè)問題之前我也沒有考慮,后來在我詢問大佬的過程中知道了,在我們存數(shù)據(jù)的時(shí)候就已經(jīng)做完這件事了,就是這句:

  1. query = { 
  2.     'article_url': item['article_url'
  3. self.collection.update_one(query, {"$set": dict(item)}, upsert=True

通過帖子的鏈接確定是否有數(shù)據(jù)爬取重復(fù),如果重復(fù)可以理解為將其覆蓋,這樣也可以做到更新數(shù)據(jù)。

6、其他設(shè)置

像多線程、headers頭,管道傳輸順序等問題,都在settings.py文件中設(shè)置,具體可以參考小編的項(xiàng)目去看,這里不再贅述。

七、效果展示

1、點(diǎn)擊運(yùn)行,結(jié)果顯示在控制臺(tái),如下圖所示。

2、中間會(huì)一直向隊(duì)列中堆很多帖子的爬取任務(wù),然后多線程處理,我設(shè)置的是16線程,速度還是很可觀的。

3、數(shù)據(jù)庫數(shù)據(jù)展示:

content_info中存放著每個(gè)帖子的全部留言以及相關(guān)用戶的公開信息。

八、總結(jié)

1、這篇文章主要給大家介紹了食品網(wǎng)站的數(shù)據(jù)采集和存儲(chǔ)過程,詳解了如何分析網(wǎng)頁結(jié)構(gòu)、爬蟲策略、網(wǎng)站類型、層級關(guān)系、爬蟲方法和數(shù)據(jù)存儲(chǔ)過程,最終實(shí)現(xiàn)將帖子的每條評論爬取到數(shù)據(jù)庫中,并且做到可以更新數(shù)據(jù),防止重復(fù)爬取,反爬等,干貨滿滿。

2、本次項(xiàng)目總的來說,不是特別難搞,只要思路對了,找到了數(shù)據(jù)規(guī)則,爬起來可以說易如反掌,覺得難只是之前沒有完整走過流程,有了這次比較水的介紹,希望能對你有所幫助,那將是我最大的榮幸。

3、遇到問題首先想的不是問同事,朋友,老師,而是去谷歌,百度,看有沒有相似的情況,看別人的經(jīng)歷,一定要學(xué)會(huì)自己發(fā)現(xiàn)問題,思考問題,解決問題,這對于之后工作有非常大的幫助(我之前就被說過還沒有脫離學(xué)生時(shí)代,就是我喜歡問同事),等網(wǎng)上查詢了一定資料了,還是沒有頭緒,再去問別人,別人也會(huì)比較愿意幫助你的~

我是杯酒先生,最后分享我的座右銘給大家:保持獨(dú)立思考,不卑不亢不慫。

最后需要本文項(xiàng)目代碼的小伙伴,請?jiān)诠娞?hào)后臺(tái)回復(fù)“食品論壇”關(guān)鍵字進(jìn)行獲取,如果在運(yùn)行過程中有遇到任何問題,請隨時(shí)留言或者加小編好友,小編看到會(huì)幫助大家解決bug噢!

 

責(zé)任編輯:武曉燕 來源: Python爬蟲與數(shù)據(jù)挖掘
相關(guān)推薦

2020-03-08 22:06:16

Python數(shù)據(jù)IP

2021-01-30 10:37:18

ScrapyGerapy網(wǎng)絡(luò)爬蟲

2021-05-10 06:48:11

Python騰訊招聘

2021-05-08 08:04:05

Python爬取素材

2023-09-21 22:08:01

2020-11-27 07:38:43

MongoDB

2021-08-09 13:31:25

PythonExcel代碼

2022-10-19 14:30:59

2018-05-16 13:50:30

Python網(wǎng)絡(luò)爬蟲Scrapy

2021-02-17 09:23:31

Python百度搜索

2020-12-17 09:40:01

Matplotlib數(shù)據(jù)可視化命令

2022-04-01 20:29:26

Pandas數(shù)據(jù)存儲(chǔ)

2011-03-28 16:14:38

jQuery

2021-02-04 09:00:57

SQLDjango原生

2021-02-06 14:55:05

大數(shù)據(jù)pandas數(shù)據(jù)分析

2022-08-04 10:39:23

Jenkins集成CD

2021-04-01 09:02:38

Python小說下載網(wǎng)絡(luò)爬蟲

2021-09-29 08:53:36

scrapy框架Twisted

2009-04-22 09:17:19

LINQSQL基礎(chǔ)

2021-08-26 09:00:48

PyechartsPython可視化
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)