案例1:爬取內容存儲為一個文件
1.建立項目
C:\pythonStudy\ScrapyProject>scrapy startproject tenCent New Scrapy project 'tenCent', using template directory 'c:\\program files\\pytho n36\\lib\\site-packages\\scrapy\\templates\\project', created in: C:\pythonStudy\ScrapyProject\tenCent You can start your first spider with: cd tenCent scrapy genspider example example.com
2.編寫item文件
import scrapy class TencentItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() # 職位名稱 position_name = scrapy.Field() # 詳情鏈接 position_link = scrapy.Field() # 職位類別 position_type = scrapy.Field() # 職位人數 position_number = scrapy.Field() # 職位地點 work_location = scrapy.Field() # 發布時間 publish_times = scrapy.Field() # 工作職責 position_duty = scrapy.Field() # 工作要求 position_require = scrapy.Field()
3.建立spider文件
C:\pythonStudy\ScrapyProject\tenCent\tenCent\spiders>scrapy genspider tencent "hr.tencent.com" Created spider 'tencent' using template 'basic' in module: tenCent.spiders.tencent
編寫spider類邏輯
from tenCent.items import TencentItem class TencentSpider(scrapy.Spider): name = 'tencent' allowed_domains = ['hr.tencent.com'] base_url = 'https://hr.tencent.com/' start_urls = ['https://hr.tencent.com/position.php'] def parse(self, response): node_list = response.xpath('//tr[@class="even"] | //tr[@class="odd"]') # 選取所有標簽tr 且class屬性等於even或odd的元素 next_page = response.xpath('//a[@id="next"]/@href').extract_first() # 選取所有標簽a且id=next,href屬性值 for node in node_list: ''' 實例化對象要放在循環里面,否則會造成item被多次賦值, 因為每次循環完畢后,請求只給了調度器,入隊,並沒有去執行請求, 循環完畢后,下載器會異步執行隊列中的請求,此時item已經為最后一條記錄, 而詳細內容根據url不同去請求的,所以每條詳細頁是完整的, 最終結果是數據內容為每頁最后一條,詳細內容與數據內容不一致, 在yield item后,會把內容寫到pipeline中 ''' item = TencentItem() item['position_name'] = node.xpath('./td[1]/a/text()').extract_first() # 獲取第一個td標簽下a標簽的文本 item['position_link'] = node.xpath('./td[1]/a/@href').extract_first() # 獲取第一個td標簽下a標簽href屬性 item['position_type'] = node.xpath('./td[2]/text()').extract_first() # 獲取第二個td標簽下文本 item['position_number'] = node.xpath('./td[3]/text()').extract_first() # 獲取第3個td標簽下文本 item['work_location'] = node.xpath('./td[4]/text()').extract_first() # 獲取第4個td標簽下文本 item['publish_times'] = node.xpath('./td[5]/text()').extract_first() # 獲取第5個td標簽下文本 # yield item 注釋yield item ,因為detail方法中yield item會覆蓋這個 yield scrapy.Request(url=self.base_url + item['position_link'] ,callback=self.detail,meta={'item':item}) # 請求詳細頁,把item傳到detail # 請求給調度器,入隊,循環結束完成后,交給下載器去異步執行,返回response yield scrapy.Request(url=self.base_url + next_page,callback=self.parse) # 請求下一頁 def detail(self, response): """ 爬取詳細內容 :param response: :return: """ print("-->detail") item = response.meta['item'] # 得到parse中的yield item item['position_duty'] = ''.join(response.xpath('//ul[@class="squareli"]')[0].xpath('./li/text()').extract()) # 轉化為字符串 item['position_require'] = ''.join(response.xpath('//ul[@class="squareli"]')[1].xpath('./li/text()').extract()) # 轉化為字符串 yield item
4.建立pipeline文件
存儲數據
# -*- coding: utf-8 -*- # Define your item pipelines here # # Don't forget to add your pipeline to the ITEM_PIPELINES setting # See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html import json class TencentPipeline(object): def open_spider(self, spider): """ # spider (Spider 對象) – 被開啟的spider # 可選實現,當spider被開啟時,這個方法被調用。 :param spider: :return: """ self.file = open('tencent.json', 'w', encoding='utf-8') json_header = '{ "tencent_info":[' self.count = 0 self.file.write(json_header) # 保存到文件 def close_spider(self, spider): """ # spider (Spider 對象) – 被關閉的spider # 可選實現,當spider被關閉時,這個方法被調用 :param spider: :return: """ json_tail = '] }' self.file.seek(self.file.tell() - 1) # 定位到最后一個逗號 self.file.truncate() # 截斷后面的字符 self.file.write(json_tail) # 添加終止符保存到文件 self.file.close() def process_item(self, item, spider): """ # item (Item 對象) – 被爬取的item # spider (Spider 對象) – 爬取該item的spider # 這個方法必須實現,每個item pipeline組件都需要調用該方法, # 這個方法必須返回一個 Item 對象,被丟棄的item將不會被之后的pipeline組件所處理。 :param item: :param spider: :return: """ content = json.dumps(dict(item), ensure_ascii=False, indent=2) + "," # 字典轉換json字符串 self.count += 1 print('content', self.count) self.file.write(content) # 保存到文件
5.設置settiing
# Crawl responsibly by identifying yourself (and your website) on the user-agent USER_AGENT = '"User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"' # 頭部信息,反爬 ITEM_PIPELINES = { 'tenCent.pipelines.TencentPipeline': 300, }
6.執行程序
C:\pythonStudy\ScrapyProject\tenCent\tenCent\spiders>scrapy crawl tencent
json文件
案例2:爬取內容存儲為兩個文件
案例2與只是把案例1中的概率頁和詳細內容頁分成兩個文件去存儲,
只有某些py文件內容有變化,以下只列舉出有變化的py文件
1.編寫item文件
用兩個類表示不同的存儲內容
import scrapy """ 職位概覽頁字段 """ class TencentItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() # 職位名稱 position_name = scrapy.Field() # 詳情鏈接 position_link = scrapy.Field() # 職位類別 position_type = scrapy.Field() # 職位人數 position_number = scrapy.Field() # 職位地點 work_location = scrapy.Field() # 發布時間 publish_times = scrapy.Field() """ 職位詳細頁字段 """ class TenDetailItem(scrapy.Item): # 工作職責 position_duty = scrapy.Field() # 工作要求 position_require = scrapy.Field()
2.編寫spider文件邏輯
# -*- coding: utf-8 -*- import scrapy from tenCent.items import TencentItem from tenCent.items import TenDetailItem print(__name__) class TencentSpider(scrapy.Spider): name = 'tencent' allowed_domains = ['hr.tencent.com'] base_url = 'https://hr.tencent.com/' start_urls = ['https://hr.tencent.com/position.php'] def parse(self, response): node_list = response.xpath('//tr[@class="even"] | //tr[@class="odd"]') # 選取所有標簽tr 且class屬性等於even或odd的元素 next_page = response.xpath('//a[@id="next"]/@href').extract_first() # 選取所有標簽a且id=next,href屬性值 for node in node_list: ''' 實例化對象要放在循環里面,否則會造成item被多次賦值, 因為每次循環完畢后,請求只給了調度器,入隊,並沒有去執行請求, 循環完畢后,下載器會異步執行隊列中的請求,此時item已經為最后一條記錄, 而詳細內容根據url不同去請求的,所以每條詳細頁是完整的, 最終結果是數據內容為每頁最后一條,詳細內容與數據內容不一致, 在yield item后,會把內容寫到pipeline中 ''' item = TencentItem() item['position_name'] = node.xpath('./td[1]/a/text()').extract_first() # 獲取第一個td標簽下a標簽的文本 item['position_link'] = node.xpath('./td[1]/a/@href').extract_first() # 獲取第一個td標簽下a標簽href屬性 item['position_type'] = node.xpath('./td[2]/text()').extract_first() # 獲取第二個td標簽下文本 item['position_number'] = node.xpath('./td[3]/text()').extract_first() # 獲取第3個td標簽下文本 item['work_location'] = node.xpath('./td[4]/text()').extract_first() # 獲取第4個td標簽下文本 item['publish_times'] = node.xpath('./td[5]/text()').extract_first() # 獲取第5個td標簽下文本 yield item yield scrapy.Request(url=self.base_url + item['position_link'] ,callback=self.detail) # 請求詳細頁 # 請求給調度器,入隊,循環結束完成后,交給下載器去異步執行,返回response # yield scrapy.Request(url=self.base_url + next_page,callback=self.parse) # 請求下一頁 def detail(self, response): """ 爬取詳細內容 :param response: :return: """ print("-->detail") item = TenDetailItem() # 實例化TenDetailItem item['position_duty'] = ''.join(response.xpath('//ul[@class="squareli"]')[0].xpath('./li/text()').extract()) # 轉化為字符串 item['position_require'] = ''.join(response.xpath('//ul[@class="squareli"]')[1].xpath('./li/text()').extract()) # 轉化為字符串 yield item
3.建立pipeline文件
存儲數據
# -*- coding: utf-8 -*- # Define your item pipelines here # # Don't forget to add your pipeline to the ITEM_PIPELINES setting # See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html import json from .items import TencentItem from .items import TenDetailItem """ 存儲職位概覽 """ class TencentPipeline(object): def open_spider(self, spider): """ # spider (Spider 對象) – 被開啟的spider # 可選實現,當spider被開啟時,這個方法被調用。 :param spider: :return: """ self.file = open('tencent.json', 'w', encoding='utf-8') json_header = '{ "tencent_info":[' self.count = 0 self.file.write(json_header) # 保存到文件 def close_spider(self, spider): """ # spider (Spider 對象) – 被關閉的spider # 可選實現,當spider被關閉時,這個方法被調用 :param spider: :return: """ json_tail = '] }' self.file.seek(self.file.tell() - 1) # 定位到最后一個逗號 self.file.truncate() # 截斷后面的字符 self.file.write(json_tail) # 添加終止符保存到文件 self.file.close() def process_item(self, item, spider): """ # item (Item 對象) – 被爬取的item # spider (Spider 對象) – 爬取該item的spider # 這個方法必須實現,每個item pipeline組件都需要調用該方法, # 這個方法必須返回一個 Item 對象,被丟棄的item將不會被之后的pipeline組件所處理。 :param item: :param spider: :return: """ if isinstance(item,TencentItem): content = json.dumps(dict(item), ensure_ascii=False, indent=2) + "," # 字典轉換json字符串 self.count += 1 print('content', self.count) self.file.write(content) # 保存到文件 ''' return item后,item會根據優先級 傳遞到下一個管道TenDetailPipeline處理 此段代碼說明當實例不屬於TencentItem時,放棄存儲json, 直接傳遞到下一個管道處理 return放在if外面,如果寫在if里面item在不屬於TencentItem實例后, item會終止傳遞,造成detail數據丟失 ''' return item """ 存儲職位詳細情況 """ class TenDetailPipeline(object): def open_spider(self, spider): """ # spider (Spider 對象) – 被開啟的spider # 可選實現,當spider被開啟時,這個方法被調用。 :param spider: :return: """ self.file = open('tendetail.json', 'w', encoding='utf-8') json_header = '{ "tendetail_info":[' self.count = 0 self.file.write(json_header) # 保存到文件 def close_spider(self, spider): """ # spider (Spider 對象) – 被關閉的spider # 可選實現,當spider被關閉時,這個方法被調用 :param spider: :return: """ json_tail = '] }' self.file.seek(self.file.tell() - 1) # 定位到最后一個逗號 self.file.truncate() # 截斷后面的字符 self.file.write(json_tail) # 添加終止符保存到文件 self.file.close() def process_item(self, item, spider): """ # item (Item 對象) – 被爬取的item # spider (Spider 對象) – 爬取該item的spider # 這個方法必須實現,每個item pipeline組件都需要調用該方法, # 這個方法必須返回一個 Item 對象,被丟棄的item將不會被之后的pipeline組件所處理。 :param item: :param spider: :return: """ if isinstance(item, TenDetailItem): ''' 得到item,判斷item實例屬於TenDetailItem,存儲json文件 如果不屬於,直接return item到下一個管道 ''' print('**'*30) content = json.dumps(dict(item), ensure_ascii=False, indent=2) + "," # 字典轉換json字符串 self.count += 1 print('content', self.count) self.file.write(content) # 保存到文件 return item
4.設置settiing
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html ITEM_PIPELINES = { # 注冊2個管道 'tenCent.pipelines.TencentPipeline': 300, 'tenCent.pipelines.TenDetailPipeline':400 # 數字越大,優先級越小,最后被執行 }
5.執行
#>scrapy crawl tencent >1.txt 2>&1 #把內容輸出到文件中