scrapy 框架
Scrapy是用純Python實現一個為了爬取網站數據、提取結構性數據而編寫的應用框架,用途非常廣泛。
框架的力量,用戶只需要定制開發幾個模塊就可以輕松的實現一個爬蟲,用來抓取網頁內容以及各種圖片,非常之方便。
Scrapy 使用了 Twisted['tw?st?d] 異步網絡框架來處理網絡通訊,可以加快我們的下載速度,不用自己去實現異步框架,并且包含了各種中間件接口,可以靈活的完成各種需求。
Scrapy架構圖
Scrapy Engine(引擎): 負責Spider、ItemPipeline、Downloader、Scheduler中間的通訊,信號、數據傳遞等。
Scheduler(調度器): 它負責接受引擎發送過來的Request請求,并按照一定的方式進行整理排列,入隊,當引擎需要時,交還給引擎。
Downloader(下載器):負責下載Scrapy Engine(引擎)發送的所有Requests請求,并將其獲取到的Responses交還給Scrapy Engine(引擎),由引擎交給Spider來處理,
Spider(爬蟲):它負責處理所有Responses,從中分析提取數據,獲取Item字段需要的數據,并將需要跟進的URL提交給引擎,再次進入Scheduler(調度器),
Item Pipeline(管道):它負責處理Spider中獲取到的Item,并進行進行后期處理(詳細分析、過濾、存儲等)的地方.
Downloader Middlewares(下載中間件):你可以當作是一個可以自定義擴展下載功能的組件。
Spider Middlewares(Spider中間件):你可以理解為是一個可以自定擴展和操作引擎和Spider中間通信的功能組件(比如進入Spider的Responses;和從Spider出去的Requests)
Scrapy的使用
- 創建項目
scrapy startproject 項目名
- 創建爬蟲文件
scrapy genspider 爬蟲名 域名
Scrapy Item pipeline(管道文件)使用
當Item在Spider中被收集之后,它將會被傳遞到Item Pipeline,這些Item Pipeline組件按定義的順序處理Item。
每個Item Pipeline都是實現了簡單方法的Python類,比如決定此Item是丟棄而存儲。以下是item pipeline的一些典型應用:
驗證爬取的數據(檢查item包含某些字段,比如說name字段)
查重(并丟棄)
將爬取結果保存到文件或者數據庫中
下載項目圖片
使用圖片管道 當使用 ImagesPipeline :
在一個爬蟲里,你抓取一個項目,把其中圖片的URL放入 image_urls 組內。
項目從爬蟲內返回,進入項目管道。
當項目進入 ImagesPipeline,image_urls 組內的URLs將被Scrapy的調度器和下載器(這意味著調度器和下載器的中間件可以復用)安排下載,當優先級更高,會在其他頁面被抓取前處理。項目會在這個特定的管道階段保持“locker”的狀態,直到完成圖片的下載(或者由于某些原因未完成下載)。
當圖片下載完,另一個組(images)將被更新到結構中。這個組將包含一個字典列表,其中包括下載圖片的信息,比如下載路徑、源抓取地址(從 image_urls 組獲得)和圖片的校驗碼。 images 列表中的圖片順序將和源 image_urls 組保持一致。如果某個圖片下載失敗,將會記錄下錯誤信息,圖片也不會出現在 images 組中。
爬蟲數據持久化保存
step1: items.py文件:自定義字段,確定要爬取的目標網站數據
step2:spiders/douban.py 文件: 爬蟲文件,在這里編寫爬蟲代碼,解析數據,獲取新的url
step3:數據持久化
方式一:將數據存入mongodb
settings.py文件: 設置文件,在這里設置User-Agent,激活管道文件
ITEM_PIPELINES = {
'XXXXXXXXXXX': 300,
}
MONGODB 主機名
MONGODB_HOST = '127.0.0.1'
MONGODB 端口號
MONGODB_PORT= 27017
數據庫名稱
MONGODB_DBNAME = "XXX"
存儲數據的表名稱
MONGODB_SHEETNAME= "xxxx"
pipelines.py管道:這里進行數據的清洗和持久化
import pymongo
from scrapy.conf import settings
class DoubanPipeline(object):
# 將數據存儲在mongodb中
def __init__(self,host,port,dbname,sheetname):
# 創建MONGODB數據庫鏈接
client = pymongo.MongoClient(host=host, port=port)
# 指定數據庫
mydb = client[dbname]
# 存放數據的集合名稱
self.mysheet = mydb[sheetname]
@classmethod
def from_crawler(cls, crawler):
host = crawler.settings["MONGODB_HOST"]
port = crawler.settings["MONGODB_PORT"]
dbname = crawler.settings["MONGODB_DBNAME"]
sheetname = crawler.settings["MONGODB_SHEETNAME"]
return cls(host,port,dbname,sheetname)
def process_item(self,item,spider):
data = dict(item)
# mongodb數據插入語句,使用save保存數據的效率會很慢,因為它需要循環便利,操作費時
self.mysheet.insert(data)
return item
方式二:將數據存入mysql數據庫
settings.py文件: 設置文件,在這里設置User-Agent,激活管道文件
ITEM_PIPELINES = {
'xxxxxxxxxxxxxxxxx': 300,
}
#關于數據庫的相關配置
MYSQL_HOST = '127.0.0.1'
MYSQL_PORT = 3306
MYSQL_USER = ''
MYSQL_PWD = ''
MYSQL_DB = ''
pipelines.py管道文件
import pymysql
class DoubanPipeline(object):
# 將數據存儲值mysql數據庫
# _mysql_exceptions.OperationalError: (1366, 是因為數據庫中的字符集與charset="utf8"不符合
def __init__(self,host,port,user,pwd,db,charset):
self.client = pymysql.Connect(host,user,pwd,db,port,charset='utf8')
self.cursor = self.client.cursor()
@classmethod
def from_crawler(cls,crawler):
host = crawler.settings['MYSQL_HOST']
port = crawler.settings['MYSQL_PORT']
user = crawler.settings['MYSQL_USER']
pwd = crawler.settings['MYSQL_PWD']
db = crawler.settings['MYSQL_DB']
return cls(host,port,user,pwd,db,charset)
def process_item(self,item,spider):
insert_sql = """
insert into doubanmovie(title, playable, content, star, commentnum, inq)
VALUE (%s, %s, %s, %s, %s, %s)
"""
try:
self.cursor.execute(insert_sql, (item['title'], item['content'], item['score'], item['info']))
self.client.commit()
except Exception as err:
print(err)
self.client.rollback()
return item
將sql語句和要插入的數據在item中定義一個方法返回,通過item調用,然后返回
class XxxxItem(scrapy.Item):
#名稱
title = scrapy.Field()
def insert_data_to_db(self,dataDict):
sql = """
INSERT INTO caipu (%s)
VALUES (%s)
""" % (','.join(dataDict.keys()),','.join(['%s']*len(dataDict)))
data = list(dataDict.values())
return sql,data
mysql數據異步插入
#twisted是一個異步的網絡框架,這里可以幫助我們實現異步將數據插入數據庫
#adbapi里面的子線程會去執行數據庫的阻塞操作,當一個線程執行完畢之后,同時,原始線程能繼續進行正常的工作,服務其他請求。
import pymysql
from twisted.enterprise import adbapi
#異步插入數據庫
class DoubanPipeline(object):
def __init__(self,dbpool):
self.dbpool = dbpool
#使用這個函數來應用settings配置文件。
@classmethod
def from_crawler(cls, crawler):
parmas = {
'host':crawler.settings['MYSQL_HOST'],
'user':crawler.settings['MYSQL_USER'],
'passwd':crawler.settings['MYSQL_PASSWD'],
'db':crawler.settings['MYSQL_DB'],
'port':3306,
'charset':'utf8',
}
# **表示字典,*tuple元組,
# 使用ConnectionPool,起始最后返回的是一個ThreadPool
dbpool = adbapi.ConnectionPool(
'pymysql',
**parmas
)
return cls(dbpool)
def process_item(self, item, spider):
#這里去調用任務分配的方法
query = self.dbpool.runInteraction(
self.insert_data_todb,
item,
spider
)
#數據插入失敗的回調
query.addErrback(
self.handle_error,
item
)
#執行數據插入的函數
def insert_data_todb(self,cursor,item,spider):
insert_str,parmas = item.insertdata()
cursor.execute(insert_str,parmas)
print('插入成功')
def handle_error(self,failure,item):
print(failure)
print('插入錯誤')
#在這里執行你想要的操作
def close_spider(self, spider):
self.pool.close()
Scrapy Shell 的使用
Scrapy終端是一個交互終端,我們可以在未啟動spider的情況下嘗試及調試代碼,也可以用來測試XPath或CSS表達式,查看他們的工作方式,方便我們爬取的網頁中提取的數據。
啟動Scrapy Shell
scrapy shell " "
scrapy shell -s USER_AGENT=" "
嘗試使用Selector
返回 xpath選擇器對象列表
response.xpath('')
使用 extract()方法返回 Unicode字符串列表(打印列表第一個元素,終端編碼格式顯示)
response.xpath(' ').extract()[0]
返回 xpath選擇器對象列表
response.xpath('//title/text()')[0].extract()
Scrapy Spider文件介紹
scrapy.Spider是最基本的類,所有編寫的爬蟲必須繼承這個類
主要用到的函數及調用順序為:
init() : 初始化爬蟲名字和start_urls列表
start_requests() 調用make_requests_from url():生成Requests對象交給Scrapy下載并返回response
parse():
解析response,并返回Item或Requests(需指定回調函數)。
Item傳給Item pipline持久化 , 而Requests交由Scrapy下載,并由指定的回調函數處理(默認parse()),一直進行循環,直到處理完所有的數據為止。
Scrapy CrawlSpiders介紹和使用
CrawlSpider它是Spider的派生類,Spider類的設計原則是只爬取start_url列表中的網頁,而CrawlSpider類定義了一些規則Rule來提供跟進鏈接的方便的機制,從爬取的網頁結果中獲取鏈接并繼續爬取的工作
#######rules
CrawlSpider使用rules屬性來決定爬蟲的爬取規則,并將匹配后的url請求提交給引擎,完成后續的爬取工作。
在rules中包含一個或多個Rule對象,每個Rule對爬取網站的動作定義了某種特定操作,比如提取當前相應內容里的特定鏈接,是否對提取的鏈接跟進爬取,對提交的請求設置回調函數等。
LinkExtractors (目的提取鏈接)
主要參數:
allow:滿足括號中“正則表達式”的URL會被提取,如果為空,則全部匹配。
deny:滿足括號中“正則表達式”的URL一定不提取(優先級高于allow)。
allow_domains:會提取的鏈接的domains。
deny_domains:一定不會被提取鏈接的domains。
restrict_xpaths:使用xpath表達式,和allow共同作用過濾鏈接。
Scrapy Request和Response相關參數介紹
Request先關參數介紹
url: 就是需要請求,并進行下一步處理的url
callback: 指定該請求返回的Response,由那個函數來處理。
method: 請求一般不需要指定,默認GET方法,可設置為"GET", "POST", "PUT"等,且保證字符串大寫
headers: 請求頭
cookies: cookies,模擬用戶登錄需要指定用戶的cookies,字典dict型
meta: 比較常用,在不同的請求之間傳遞數據使用的。字典dict型
encoding: 編碼類型,使用默認的 'utf-8' 就行。
dont_filter: 表明該請求不由調度器過濾。這是當你想使用多次執行相同的請求,忽略重復的過濾器。默認為False。
errback: 指定錯誤處理函數
Response相關參數介紹
status: 響應碼
body: 響應體
url:響應url
self.request (request對象)
self.headers (響應頭)
Scrapy DOWNLOADER_MIDDLEWARE 的使用
通常防止爬蟲被反主要有以下幾個策略
- 動態設置User-Agent(隨機切換User-Agent,模擬不同用戶的瀏覽器信息)
*禁用Cookies(前提是爬取的網站不需要cookies參數)(也就是不啟用cookies middleware,不向Server發送cookies,有些網站通過cookie的使用發現爬蟲行為)
使用cookies池,自定義中間件
除非特殊需要,否則禁用cookies,防止某些網站根據Cookie來封鎖爬蟲。
COOKIES_ENABLED = False
設置延遲下載(降低訪問網站的頻率)(設置為2秒或更高)
DOWNLOAD_DELAY = 2
使用IP代理地址池:VPN和代理IP,現在大部分網站都是根據IP來反爬的。