在沒有系統學習框架進行爬蟲的時候,已經可以使用selenium對動態渲染的網頁和Ajax加載數據的網頁進行爬取,但代碼的整體邏輯在於自己對於方法的認識編寫和使用,沒有很強的邏輯框架性,在自學Scrapy框架的時候也學到對於Selenium的對接,最近進行了學習,現在和大家分享,有什麼不足或錯誤的地方請評論或私信喲😁
使用selenium對網頁抓取的原理在於,使用selenium模擬登陸瀏覽器進行抓取,不需要關心頁面後臺發生的請求,也不需要分析頁面渲染過程,只需要關注頁面最終結果即可,在進行網頁跳轉的時候,也只需要關心URL的變化規律,從而來構造請求列表來重複進行請求,也不需要關心如何去構造請求頭。
Scrapy對接Selenium
1. 新建項目
scrapy startproject scrapyselenium(項目名)
2. 創建 Spider
直接用Pycharm打開上一步所創建的項目,在最下面的Terminal處執行該命令:
scrapy genspider xiaozhu www.xiaozhu.com
3. 網頁的分析和自己爬取數據的確定
目標網頁URL(第一頁):
url = 'https://cq.xiaozhu.com/search-duanzufang-p0-0/'
這個網頁不是小豬短租的官網,這是在選擇了地點(我選地點是重慶)後出現的頁面的URL,因爲我是爲了去熟悉使用並瞭解Scrapy對接selenium,所以就直接偷懶選擇了固定地點後的頁面去進行分析。
由於是使用selenium進行可見可爬,所以不需要進行一系列的關於Request和Response請求的分析,轉向尋找URL變化的規律,從而來構建Request的請求列表:
https://cq.xiaozhu.com/search-duanzufang-p0-0/
https://cq.xiaozhu.com/search-duanzufang-p1-0/
https://cq.xiaozhu.com/search-duanzufang-p2-0/
可以發現它的變化僅僅在於p0,p1的變化,用來控頁數,接着我們就來看看自己所要的信息在哪,這裏不建議直接f12去查看源代碼,這個是輔助查詢,主要代碼依據是在服務器返回的請求響應中去查看源代碼,源代碼很可能在第一個響應中:
並發現每一個房子的相關信息都存在 li 標籤,class爲current的標籤當中,然後我就開始下一步的分析。
4. 定義Item
爲要爬取的數據進行字段的定義。
import scrapy
from scrapy import Field
class HouseItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
house_name = Field()
#location = Field()
house_price = Field()
house_href = Field()
house_size = Field()
5. 構造 Request 請求列表
根據URL連接變化的規律來在 Spider中進行構造:
#這裏ragne函數代表了頁數的變化,爲了測試,我這裏只定義了一頁的數據
def start_requests(self):
for i in range(1):
i = str(i)
url = 'https://cq.xiaozhu.com/search-duanzufang-p' + i + '-0/'
yield Request(url=url,callback=self.parse)
6. 對接 Selenium (說明在哪裏進行對接爲什麼在這裏)
在構造完請求列表後,就要對這些請求進行抓取,從Scrapy整體框架和數據流的流向進行分析,當引擎從Scheduler中調度出請求後,請求將會被髮往Downloader進行下載,在被下載之前會經過 Downloader Middleware下載器中間件進行請求的處理,我們變可以在這裏對請求進行攔截,處理這個請求,使用Selenium對請求進行處理,最後返回HttpResponse請求,直接跳過下載器,直接交給Spider進行處理。
要先對這個請求進行攔截,我們需要在 middleware.py 中進行代碼的編寫,定義自己的中間類並進行啓動,因爲是處理到達下載器之前的Request請求,所以需要重寫 process_request() 方法。
對於Scrapy項目運行數據流總覽 AND 幾個重要的組件、中間件分析,我的這篇博文已經給出:
https://blog.csdn.net/zc666ying/article/details/106601578
# 因爲目的是測試並瞭解對接Selenium,並未做出任何模擬的點擊,簡單明瞭直接獲取網頁源碼並返回
class SeleniumMiddleware(object):
def process_request(self,request,spider):
driver = webdriver.Chrome()
#driver.maximize_window()
driver.get(request.url)
time.sleep(3)
text = driver.page_source
driver.quit()
# https://doc.scrapy.org/en/latest/topics/request-response.html
# 查看 HtmlResponse 對象結構
return HtmlResponse(url=request.url,encoding='utf-8',body=text,request=request)
6.1 啓動自己所編寫的 Downloader Middlewares
在 settings.py 中進行設置:
DOWNLOADER_MIDDLEWARES = {
'scrapyselenium.middlewares.SeleniumMiddleware':543,
}
7. 對頁面進行解析
由於上面說到所返回的Response對象會直接給Spider,所以直接編寫解析函數 parse():
def parse(self, response):
house_name = response.xpath('//img[@class="lodgeunitpic"]/@title').extract()
house_price = response.xpath('//span[@class="result_price"]//i/text()').extract()
house_size = response.xpath('//em[@class="hiddenTxt"]/text()').extract()
house_href = response.xpath('//a[contains(@target,"_blank") and @class="resule_img_a"]/@href').extract()
for i in range(len(house_name)):
item = HouseItem(
house_name = house_name[i],
house_price = house_price[i],
house_size = house_size[i],
house_href = house_href[i]
)
yield item
8. 將數據存入數據庫當中(Item Pipeline)
8.1 定義自己的 Item Pipeline
import pymysql
class PymysqlPipeline(object):
#連接數據庫
def __init__(self):
self.connect = pymysql.connect(
host = 'localhost',
database = 'xiaozhu_house',
user = 'root',
password = '123456',
charset = 'utf8',
port = 3306
)
# 創建遊標對象
self.cursor = self.connect.cursor()
# 此方法是必須要實現的方法,被定義的 Item Pipeline 會默認調用這個方法對 Item 進行處理
def process_item(self,item,spider):
cursor = self.cursor
sql = 'insert into house_information(house_name,house_price,house_size,house_href) values (%s,%s,%s,%s)'
cursor.execute(sql,(
item['house_name'],item['house_price'],item['house_size'],item['house_href']
))
# 提交數據庫事務
self.connect.commit()
return item
8.2 啓動Item Pipeline
在 settings.py 中進行設置:
ITEM_PIPELINES = {
'scrapyselenium.pipelines.PymysqlPipeline':300
}
9. 運行
scrapy crawl xiaozhu
10. 運行結果