scrapy爬蟲框架
- 2019 年 10 月 3 日
- 筆記
scrapy框架是異步處理框架,可配置和可擴展程度非常高,Python中使用最廣泛的爬蟲框架。
安裝
Ubuntu安裝
1、安裝依賴包
- sudo apt-get install libffi-dev
- sudo apt-get install libssl-dev
- sudo apt-get install libxml2-dev
- sudo apt-get install python3-dev
- sudo apt-get install libxslt1-dev
- sudo apt-get install zlib1g-dev
- sudo pip3 install -I -U service_identity
2、安裝scrapy框架
- sudo pip3 install Scrapy
Windows安裝
cmd命令行(管理員): python -m pip install Scrapy
Scrapy框架五大組件
- 引擎(Engine) :整個框架核心
- 調度器(Scheduler) :維護請求隊列
- 下載器(Downloader) :獲取響應對象,下載器是基於多線程的
- 爬蟲文件(Spider) :數據解析提取
- 項目管道(Pipeline) :數據入庫處理
下載器中間件(Downloader Middlewares) : 引擎->下載器,包裝請求(隨機代理等)
蜘蛛中間件(Spider Middlewares) : 引擎->爬蟲文件,可修改響應對象屬性
scrapy爬蟲工作流程
爬蟲項目啟動
1、由引擎向爬蟲程序索要第一個要爬取的URL,交給調度器去入隊列
2、調度器處理請求後出隊列,通過下載器中間件交給下載器去下載
3、下載器得到響應對象後,通過蜘蛛中間件交給爬蟲程序
4、爬蟲程序進行數據提取:
1、數據交給管道文件去入庫處理
2、對於需要繼續跟進的URL,再次交給調度器入隊列,依次循環
scrapy常用命令
1、創建爬蟲項目 scrapy startproject 項目名
2、創建爬蟲文件 scrapy genspider 爬蟲名 域名
域名為協議後面的名字
3、運行爬蟲 scrapy crawl 爬蟲名
在cmd窗口運行上面指令後,會在當前文件夾自動創建如下目錄結構。
scrapy項目目錄結構
Baidu # 項目文件夾
├── Baidu # 項目目錄
│ ├── items.py # 定義數據結構
│ ├── middlewares.py # 中間件
│ ├── pipelines.py # 數據處理
│ ├── settings.py # 全局配置
│ └── spiders
│ ├── baidu.py # 爬蟲文件
└── scrapy.cfg # 項目基本配置文件
全局配置文件settings.py詳解
1、定義User-Agent
USER_AGENT = ‘Mozilla/5.0’
2、是否遵循robots協議,一定要設置為False
ROBOTSTXT_OBEY = False
3、最大並發量,默認為16
CONCURRENT_REQUESTS = 32
4、下載延遲時間
DOWNLOAD_DELAY = 1
5、請求頭,此處也可以添加User-Agent
DEFAULT_REQUEST_HEADERS={}
6、項目管道,運行管道函數
ITEM_PIPELINES={
’項目目錄名.pipelines.類名’:300
}
創建爬蟲項目步驟
- 新建項目 :scrapy startproject 項目名
- cd 項目文件夾
- 新建爬蟲文件 :scrapy genspider 文件名 域名
- 明確目標(items.py)
- 寫爬蟲程序(文件名.py)
- 管道文件(pipelines.py)
- 全局配置(settings.py)
- 運行爬蟲 :scrapy crawl 爬蟲名
pycharm運行爬蟲項目
1、創建一個腳本文件,比如:begin.py(和scrapy.cfg文件同目錄)
2、begin.py中內容:
from scrapy import cmdline cmdline.execute('scrapy crawl maoyan'.split())
導入cmd命令行模塊,在python中寫cmd命令,之所以用split()是因為把字符串按空格切割,這樣cmd才能識別是3個參數。
百度
目標:打開百度首頁,把 ‘百度一下,你就知道’ 抓取下來,從終端輸出
實現步驟
1、創建項目Baidu 和 爬蟲文件baidu
1、scrapy startproject Baidu
2、cd Baidu
3、scrapy genspider baidu www.baidu.com
2、編寫爬蟲文件baidu.py,xpath提取數據
# -*- coding: utf-8 -*- import scrapy class BaiduSpider(scrapy.Spider): name = 'baidu' # 爬蟲名 : scrapy crawl 爬蟲名 allowed_domains = ['www.baidu.com'] # 允許爬取的域名 start_urls = ['http://www.baidu.com/'] # 起始URL地址 def parse(self, response): # response為百度的響應對象,提取"百度一下,你就知道" # r_list: [<selector xpath='',data=''>] # extract(): ["百度一下,你就知道"] # extract_first(): "百度一下,你就知道" # 1.6版本後可使用get(): "百度一下,你就知道" r_list = response.xpath('/html/head/title/text()').get() print('*'*50) print(r_list) print('*'*50)
3、全局配置settings.py
USER_AGENT = 'Mozilla/5.0' ROBOTSTXT_OBEY = False DEFAULT_REQUEST_HEADERS = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'en', }
或者把USER_AGENT文件寫道頭文件裏面
DEFAULT_REQUEST_HEADERS = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'en', 'USER_AGENT':'Mozilla/5.0', }
4、創建begin.py(和scrapy.cfg同目錄)
from scrapy import cmdline cmdline.execute('scrapy crawl baidu'.split())
5、啟動爬蟲
直接運行 begin.py 文件即可
貓眼電影案例
目的
- URL: 百度搜索 -> 貓眼電影 -> 榜單 -> top100榜
- 爬取內容:電影名稱、電影主演、上映時間
實現步驟
1、創建項目和爬蟲文件
創建爬蟲項目 scrapy startproject Maoyan
cd Maoyan
創建爬蟲文件 scrapy genspider maoyan maoyan.com
2、定義要爬取的數據結構(items.py)
name = scrapy.Field() star = scrapy.Field() time = scrapy.Field()
3、編寫爬蟲文件(maoyan.py)
1、基準xpath,匹配每個電影信息節點對象列表
dd_list = response.xpath(‘//dl[@class=”board-wrapper”]/dd’)
2、for dd in dd_list:
電影名稱 = dd.xpath(‘./a/@title’)
電影主演 = dd.xpath(‘.//p[@class=”star”]/text()’)
上映時間 = dd.xpath(‘.//p[@class=”releasetime”]/text()’)
代碼實現一
下載速度慢,爬了一頁再爬第二頁,調度器裏面只有一個地址。
# -*- coding: utf-8 -*- import scrapy from ..items import MaoyanItem class MaoyanSpider(scrapy.Spider): name = 'maoyan' # 爬蟲名 allowed_domains = ['maoyan.com'] # 允許爬蟲的域名 start_urls = ['https://maoyan.com/board/4?offset=0'] # 起始的URL地址 offset = 0 def parse(self, response): # 給items.py中的類:MaoyanItem(scrapy.Item)實例化 item = MaoyanItem() # 基準xpath,匹配每個電影信息節點對象列表 dd_list = response.xpath('//dl[@class="board-wrapper"]/dd') # 依次遍歷 for dd in dd_list: # [<selector xpath='' data='霸王別姬'>] # dd.xpath('')結果為[選擇器1,選擇器2] # .extract() 把[選擇器1,選擇器2]所有選擇器序列化為unicode字符串 # .extract_first() : 取第一個字符串 # 是在給items.py中那些類變量賦值 item['name'] = dd.xpath('./a/@title').get().strip() item['star'] = dd.xpath('.//p[@class="star"]/text()').get().strip() item['time'] = dd.xpath('.//p[@class="releasetime"]/text()').get().strip() # 把item對象交給管道文件處理 yield item # 此方法不推薦,效率低 self.offset += 10 if self.offset <= 91: url = 'https://maoyan.com/board/4?offset={}'.format(self.offset) # 交給調度器入隊列 yield scrapy.Request( url=url, callback=self.parse)
代碼實現二,基於下載器是多線程的,把多個地址,一次性的都給調度器,請求指紋,第一個地址爬了兩次
# -*- coding: utf-8 -*- import scrapy from ..items import MaoyanItem class MaoyanSpider(scrapy.Spider): name = 'maoyan2' # 爬蟲名 allowed_domains = ['maoyan.com'] # 允許爬取的域名 start_urls = ['https://maoyan.com/board/4?offset=0'] # 起始的URL地址 def parse(self, response): for offset in range(0, 91, 10): url = 'https://maoyan.com/board/4?offset={}'.format(offset) # 把地址交給調度器入隊列 yield scrapy.Request(url=url, callback=self.parse_page) def parse_page(self, response): # 給items.py中的類:MaoyanItem(scrapy.Item)實例化 item = MaoyanItem() # 基準xpath,匹配每個電影信息節點對象列表 dd_list = response.xpath('//dl[@class="board-wrapper"]/dd') # dd_list : [<element dd at xxx>,<...>] for dd in dd_list: # [<selector xpath='' data='霸王別姬'>] # dd.xpath('')結果為[選擇器1,選擇器2] # .extract() 把[選擇器1,選擇器2]所有選擇器序列化為 # unicode字符串 # .extract_first() : 取第一個字符串 # 是在給items.py中那些類變量賦值 item['name'] = dd.xpath('./a/@title').get().strip() item['star'] = dd.xpath('.//p[@class="star"]/text()').get().strip() item['time'] = dd.xpath('.//p[@class="releasetime"]/text()').get().strip() # 把item對象交給管道文件處理 yield item
代碼實現三
# 重寫start_requests()方法,直接把多個地址都交給調度器去處理 import scrapy from ..items import MaoyanItem class MaoyanSpider(scrapy.Spider): name = 'maoyan3' # 爬蟲名 allowed_domains = ['maoyan.com'] # 允許爬取的域名 # 去掉start_urls變量 # 重寫start_requests()方法,把所有URL地址都交給調度器
# 去掉start_urls
def start_requests(self): for offset in range(0, 91, 10): url = 'https://maoyan.com/board/4?offset={}'.format(offset) yield scrapy.Request(url=url, callback=self.parse) # 把地址交給調度器入隊列 def parse(self, response): item = MaoyanItem() # 給items.py中的類:MaoyanItem(scrapy.Item)實例化 # 基準xpath dd_list = response.xpath('//dl[@class="board-wrapper"]/dd') # 依次遍歷 for dd in dd_list: # [<selector xpath='' data='霸王別姬'>] # dd.xpath('')結果為[選擇器1,選擇器2] # .extract() 把[選擇器1,選擇器2]所有選擇器序列化為 # unicode字符串 # .extract_first() : 取第一個字符串 # 是在給items.py中那些類變量賦值 item['name'] = dd.xpath('./a/@title').get().strip() item['star'] = dd.xpath('.//p[@class="star"]/text()').get().strip() item['time'] = dd.xpath('.//p[@class="releasetime"]/text()').get().strip() yield item # 把item對象交給pipline管道文件處理
3、定義管道文件(pipelines.py)
# -*- 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 pymysql from .settings import * class MaoyanPipeline(object): # item: 從爬蟲文件maoyan.py中yield的item數據 def process_item(self, item, spider): print(item['name'], item['time'], item['star']) return item # 新建自定義管道 - 存入MySQL數據庫 class MaoyanMysqlPipeline(object): # 爬蟲項目開始運行時執行此函數 def open_spider(self, spider): print('我是open_spider函數輸出') # 一般用於建立數據庫連接 self.db = pymysql.connect( host=MYSQL_HOST, user=MYSQL_USER, password=MYSQL_PWD, database=MYSQL_DB, charset=MYSQL_CHAR) self.cursor = self.db.cursor() def process_item(self, item, spider): # 因為execute()的第二個參數為列表 L = [item['name'].strip(), item['star'].strip(), item['time'].strip()] self.cursor.execute('insert into filmtab values(%s,%s,%s)', L) self.db.commit() # 提交到數據庫 return item # 爬蟲項目結束時執行此函數,只執行一次 def close_spider(self, spider): print('我是close_spider函數輸出') # 一般用於斷開數據庫連接 self.cursor.close() self.db.close()
5、全局配置文件(settings.py)
ROBOTSTXT_OBEY = False DEFAULT_REQUEST_HEADERS = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'en', '’USER_AGENT' = 'Mozilla/5.0' } ITEM_PIPELINES = {
# 300表示優先級(1-1000),數字越小,優先級越高 'Maoyan.pipelines.MaoyanPipeline': 300, 'Maoyan.pipelines.MaoyanMysqlPipeline': 200}
6、創建並運行文件(begin.py)
from scrapy import cmdline cmdline.execute('scrapy crawl maoyan'.split())
爬蟲項目啟動方式
方式一
從爬蟲文件(spider)的start_urls變量中遍歷URL地址,把下載器返回的響應對象(response)交給爬蟲文件的parse()函數處理
# start_urls = [‘http://www.baidu.com/’]
方式二
重寫start_requests()方法,從此方法中獲取URL,交給指定的callback解析函數處理
1、去掉start_urls變量
2、def start_requests(self):
# 生成要爬取的URL地址,利用scrapy.Request()方法交給調度器 **
知識點匯總
response.xpath(”)調用方法
結果:列表,元素為選擇器 [‘<selector xpath=” data=’A’>]
.extract() :提取文本內容,序列化列表中所有選擇器為Unicode字符串 [‘A’,’B’,’C’]
.extract_first() 或者 get() :獲取列表中第1個序列化的元素(字符串)
.get():提取列表中第1個文本內容
response.text:獲取響應內容
response.body:獲取bytes數據類型
response.xpath(”)
pipelines.py中必須由1個函數叫process_item
def process_item(self,item,spider): return item ( * 此處必須返回 item )
日誌變量及日誌級別(settings.py)
# 日誌相關變量
LOG_LEVEL = ”
LOG_LEVEL = ‘INFO’ # 表示終端只顯示INFO和INF日誌級別以上的信息,DEBUG就不會顯示了
LOG_FILE : 本來應該輸出在終端的信息,寫入到了log日誌文件中
LOG_FILE = ‘文件名.log’
# 日誌級別
5 CRITICAL :嚴重錯誤
4 ERROR :普通錯誤
3 WARNING :警告
2 INFO :一般信息
1 DEBUG :調試信息
settings.py常用變量
LOG_LEVEL = '' # 1、設置日誌級別 LOG_FILE = '' # 2、保存到日誌文件(不在終端輸出) FEED_EXPORT_ENCODING = '' # 3、設置數據導出編碼(主要針對於json文件) IMAGES_STORE = '路徑' # 4、非結構化數據存儲路徑 USER_AGENT = '' # 5、設置User-Agent CONCURRENT_REQUESTS = 32 # 6、設置最大並發數(默認為16) # 7、下載延遲時間(每隔多長時間請求一個網頁) # DOWNLOAD_DELAY 會影響 CONCURRENT_REQUESTS,不能使並發顯現 # 有CONCURRENT_REQUESTS,沒有DOWNLOAD_DELAY: 服務器會在同一時間收到大量的請求 # 有CONCURRENT_REQUESTS,有DOWNLOAD_DELAY 時,服務器不會在同一時間收到大量的請求 DOWNLOAD_DELAY = 3 DEFAULT_REQUEST_HEADERS = {} # 8、請求頭 ITEM_PIPELINES = {} # 9、添加項目管道 DOWNLOADER_MIDDLEWARES = {} # 10、添加下載器中間件
管道處理數據流程
1、在爬蟲文件中為items.py中類做實例化,用爬下來的數據給對象賦值
from ..items import MaoyanItem
item = MaoyanItem()
item[‘name’] = xxx
2、管道文件(pipelines.py)
3、開啟管道(settings.py)
ITEM_PIPELINES = { ‘項目目錄名.pipelines.類名’:優先級 }
優先級1-1000,數字越小優先級越高
scrapy.Request()參數
1、url
2、callback
3、meta:傳遞數據,定義代理
數據持久化存儲(MySQL)
實現步驟
1、在setting.py中定義MYSQL相關變量
# 定義MySQL相關變量 MYSQL_HOST = '127.0.0.1' MYSQL_USER = 'root' MYSQL_PWD = '123456' MYSQL_DB = 'maoyandb' MYSQL_CHAR = 'utf8'
2、pipelines.py中新建管道類,並導入settings模塊from .settings import *
# 新建自定義管道 - 存入MySQL數據庫 class MaoyanMysqlPipeline(object): # 爬蟲程序啟動時,只執行1次,一般用於數據庫連接 def open_spider(self, spider): print('我是open_spider函數輸出') # 一般用於建立數據庫連接 self.db = pymysql.connect( host=MYSQL_HOST, user=MYSQL_USER, password=MYSQL_PWD, database=MYSQL_DB, charset=MYSQL_CHAR) self.cursor = self.db.cursor() def process_item(self, item, spider): # 用於處理爬取的item數據,這個函數一定要有 # 因為execute()的第二個參數為列表 L = [item['name'].strip(), item['star'].strip(), item['time'].strip()] self.cursor.execute('insert into filmtab values(%s,%s,%s)', L) self.db.commit() # 提交到數據庫 return item # 爬蟲項目結束時執行此函數,只執行一次,一般用於斷開數據庫連接 def close_spider(self, spider): print('我是close_spider函數輸出') # 一般用於斷開數據庫連接 self.cursor.close() self.db.close()
注意 :process_item() 函數中一定要 return item
3、settings.py中添加此管道
ITEM_PIPELINES = { 'Maoyan.pipelines.MaoyanPipeline': 300, 'Maoyan.pipelines.MaoyanMysqlPipeline': 200 # 數據庫的管道 }
注意 :process_item() 函數中一定要 return item,因為第一個管道返回的item會繼續交由下一個管道處理,否則返回並傳入下一個管道的值為None
保存為csv、json文件
scrapy crawl maoyan -o maoyan.csv
scrapy crawl maoyan -o maoyan.json
# 在存json文件的時候,要在setting.py設置到處編碼 FEED_EXPORT_ENCODING = ‘utf-8’
盜墓筆記小說抓取案例(三級頁面)
目標
# 抓取目標網站中盜墓筆記1-8中所有章節的所有小說的具體內容,保存到本地文件
1、網址 :http://www.daomubiji.com/
準備工作xpath
1、一級頁面xpath(此處響應做了處理):
盜墓筆記1-8的鏈接://ul[@class=”sub-menu”]/li/a/@href
2、二級頁面xpath:/html/body/section/div[2]/div/article
基準xpath ://article
鏈接:./a/@href
標題:./a/text() # 七星魯王 第一章 血屍
3、三級頁面xpath:
response.xpath(‘//article[@class=”article-content”]//p/text()’).extract()
項目實現
1、創建項目及爬蟲文件
創建項目 :scrapy startproject Daomu
創建爬蟲 :scrapy genspider daomu www.daomubiji.com
2、定義要爬取的數據結構(把數據交給管道)items.py
import scrapy class DaomuItem(scrapy.Item): juan_name = scrapy.Field() # 卷名 zh_num = scrapy.Field() # 章節數 zh_name = scrapy.Field() # 章節名 zh_link = scrapy.Field() # 章節鏈接 zh_content = scrapy.Field() # 小說內容
3、爬蟲文件實現數據抓取 daomu.py
# -*- coding: utf-8 -*- import scrapy from ..items import DaomuItem class DaomuSpider(scrapy.Spider): name = 'daomu' allowed_domains = ['www.daomubiji.com'] start_urls = ['http://www.daomubiji.com/'] # 解析一級頁面,提取 盜墓筆記1 2 3 ... 鏈接 def parse(self, response): one_link_list = response.xpath('//ul[@class="sub-menu"]/li/a/@href').extract() print(one_link_list) # 把鏈接交給調度器入隊列 for one_link in one_link_list: yield scrapy.Request(url=one_link, callback=self.parse_two_link, dont_filter=True) # 解析二級頁面 def parse_two_link(self,response): # 基準xpath,匹配所有章節對象列表 article_list = response.xpath('/html/body/section/div[2]/div/article') # 依次獲取每個章節信息 for article in article_list: # 創建item對象 item = DaomuItem() info = article.xpath('./a/text()').extract_first().split() # info : ['七星魯王','第一章','血屍'] item['juan_name'] = info[0] item['zh_num'] = info[1] item['zh_name'] = info[2] item['zh_link'] = article.xpath('./a/@href').extract_first() # 把章節鏈接交給調度器 yield scrapy.Request( url=item['zh_link'], # 把item傳遞到下一個解析函數 meta={'item':item}, callback=self.parse_three_link, dont_filter=True ) # 解析三級頁面 def parse_three_link(self,response): # 獲取上一個函數傳遞過來的item對象 item = response.meta['item'] # 獲取小說內容 # ['段落1','段落2','段落3',....] item['zh_content'] = 'n'.join(response.xpath( '//article[@class="article-content"]//p/text()' ).extract()) # 所有的數據都爬完了,再yield yield item # 'n'.join(['第一段','第二段','第三段'])
4、管道文件實現數據處理pipline.py
# -*- 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 class DaomuPipeline(object): def process_item(self, item, spider): filename = '/home/tarena/aid1902/{}-{}-{}.txt'.format( item['juan_name'], item['zh_num'], item['zh_name'] ) f = open(filename,'w') f.write(item['zh_content']) f.close() return item
5、setting
打開通道
騰訊招聘
MySQL數據庫–建庫建表
create database tencentdb charset utf8; use tencentdb; create table tencenttab( name varchar(100), type varchar(100), duty varchar(5000), requirement varchar(5000) )charset=utf8;
1、創建項目+爬蟲文件
scrapy startproject Tencent cd Tencent scrapy genspider tencent hr.tencent.com
2、定義爬取的數據結構 items.py
job_name = scrapy.Field() job_type = scrapy.Field() # 類別 job_duty = scrapy.Field() # 職責 job_require = scrapy.Field() # 要求 job_address = scrapy.Field() # 地址
3、爬蟲文件
class TencentSpider(scrapy.Spider): name = 'tencent' allowed_domains = ['careers.tencent.com'] one_url = 'https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1563912271089&countryId=&cityId=&bgIds=&productId=&categoryId=&parentCategoryId=&attrId=&keyword=&pageIndex={}&pageSize=10&language=zh-cn&area=cn' two_url = 'https://careers.tencent.com/tencentcareer/api/post/ByPostId?timestamp=1563912374645&postId={}&language=zh-cn' # 1. 去掉start_urls # 2. 重新start_requests()方法 def start_requests(self): total_page = self.get_total_page() for page_index in range(1,total_page): url = self.one_url.format(page_index) yield scrapy.Request( url = url, callback = self.parse_one ) # 獲取總頁數 def get_total_page(self): url = self.one_url.format(1) html = requests.get(url=url).json() total_page = int(html['Data']['Count']) // 10 + 1 return total_page # 解析一級頁面函數 def parse_one(self,response): html = json.loads(response.text) for job in html['Data']['Posts']: item = TencentItem() # postId: 拼接二級頁面的地址 post_id = job['PostId'] two_url = self.two_url.format(post_id) # 交給調度器 yield scrapy.Request( url = two_url, meta = {'item':item}, callback = self.parse_two_page ) def parse_two_page(self,response): item = response.meta['item'] html = json.loads(response.text) item['job_name'] = html['Data']['RecruitPostName'] item['job_type'] = html['Data']['CategoryName'] item['job_duty'] = html['Data']['Responsibility'] item['job_require'] = html['Data']['Responsibility'] item['job_address'] = html['Data']['LocationName'] yield item
4、管道文件
create database tencentdb charset utf8; use tencentdb; create table tencenttab( job_name varchar(500), job_type varchar(100), job_duty varchar(1000), job_require varchar(1000), job_address varchar(100) )charset=utf8;
管道文件pipelines實現
import pymysql class TencentMysqlPipeline(object): def open_spider(self,spider): self.db = pymysql.connect( '127.0.0.1','root','123456','tencentdb', charset='utf8' ) self.cursor = self.db.cursor() def process_item(self,item,spider): ins = 'insert into tencenttab values(%s,%s,%s,%s,%s)' job_list = [ item['job_name'],item['job_type'],item['job_duty'], item['job_require'],item['job_address'] ] self.cursor.execute(ins,job_list) self.db.commit() return item def close_spider(self,spider): self.cursor.close() self.db.close()
5、settings.py
定義常用變量,添加管道即可
圖片管道(360圖片抓取案例)
目標:www.so.com -> 圖片 -> 美女
抓取網絡數據包
2、F12抓包,抓取到json地址 和 查詢參數(QueryString)
url = ‘http://image.so.com/zj?ch=beauty&sn={}&listtype=new&temp=1’.format(str(sn))
ch: beauty
sn: 90
listtype: new
temp: 1
項目實現
1、創建爬蟲項目和爬蟲文件
scrapy startproject So cd So scrapy genspider so image.so.com
2、定義要爬取的數據結構(items.py)
img_link = scrapy.Field()
3、爬蟲文件實現圖片鏈接抓取
# -*- coding: utf-8 -*- import scrapy import json from ..items import SoItem class SoSpider(scrapy.Spider): name = 'so' allowed_domains = ['image.so.com'] # 重寫Spider類中的start_requests方法 # 爬蟲程序啟動時執行此方法,不去找start_urls def start_requests(self): for page in range(5): url = 'http://image.so.com/zj?ch=beauty&sn={}&listtype=new&temp=1'.format(str(page*30)) # 把url地址入隊列 yield scrapy.Request( url = url, callback = self.parse_img ) def parse_img(self, response): html = json.loads(response.text) for img in html['list']: item = SoItem() # 圖片鏈接 item['img_link'] = img['qhimg_url'] yield item
4、管道文件(pipelines.py)
from scrapy.pipelines.images import ImagesPipeline import scrapy class SoPipeline(ImagesPipeline): # 重寫get_media_requests方法 def get_media_requests(self, item, info): yield scrapy.Request(item['img_link'])
5、設置settings.py
IMAGES_STORE = '/home/tarena/images/'
6、創建run.py運行爬蟲
scrapy shell的使用
基本使用
- scrapy shell URL地址
- request.headers :請求頭(字典)
- reqeust.meta :item數據傳遞,定義代理(字典)
- response.text :字符串
- response.body :bytes
- response.xpath(”)
scrapy.Request()
- url
- callback
- headers
- meta :傳遞數據,定義代理
- dont_filter :是否忽略域組限制,默認False檢查域組限制allowed_domains[”]
設置中間件(隨機User-Agent)
少量User-Agent切換
方法一
# settings.py USER_AGENT = '' DEFAULT_REQUEST_HEADERS = {}
方法二
# spider yield scrapy.Request(url,callback=函數名,headers={})
大量User-Agent切換(中間件)
middlewares.py設置中間件
1、獲取User-Agent
# 方法1 :新建useragents.py,存放大量User-Agent,random模塊隨機切換
# 方法2 :安裝fake_useragent模塊(sudo pip3 install fack_useragent)
from fake_useragent import UserAgent ua_obj = UserAgent() ua = ua_obj.random
2、middlewares.py新建中間件類,攔截傳給下載器的請求內容
class RandomUseragentMiddleware(object): def process_request(self,reuqest,spider): ua = UserAgent() request.headers['User-Agent'] = ua.random
3、settings.py添加此下載器中間件
DOWNLOADER_MIDDLEWARES = { 'Baidu.middlewares.TestDownloaderMiddleware': 543, 'Baidu.middlewares.RandomUaDownloaderMiddleware': 300, 'Baidu.middlewares.TestRandomProxyMiddleware': 400, }
設置中間件(隨機代理)
import random from fake_useragent import UserAgent # 隨機User-Agent下載器中間件 class RandomUaDownloaderMiddleware(object): def process_request(self, request, spider): # 給每一個攔截下來的請求包裝隨機User-Agent ua = UserAgent() useragent = ua.random # request.headers: 字典 request.headers['User-Agent'] = useragent print('我是中間件:', useragent) # 測試 proxy_list = ['http://1.1.1.1:1111', 'http://2.2.2.2:2222'] # 隨機代理IP下載器中間件 class TestRandomProxyMiddleware(object): def process_request(self, request, spider): proxy = random.choice(proxy_list) # 1. 隨機選擇並定義好代理 request.meta['proxy'] = proxy # 2. 如何包裝 print('我是中間件2:', proxy) # 測試 # 處理異常,一旦代理不能用,則返回請求再次執行下載器中間件,把請求扔回調度器 def process_exception(self, request, exception, spider): return request