首頁 > 軟體

Scrapy爬蟲框架整合selenium及全面詳細講解

2022-04-12 19:00:59

一、架構介紹

Scrapy一個開源和共同作業的框架,其最初是為了頁面抓取 (更確切來說, 網路抓取 )所設計的,使用它可以以快速、簡單、可延伸的方式從網站中提取所需的資料。但目前Scrapy的用途十分廣泛,可用於如資料探勘、監測和自動化測試等領域,也可以應用在獲取API所返回的資料(例如 Amazon Associates Web Services ) 或者通用的網路爬蟲。

Scrapy 是基於twisted框架開發而來,twisted是一個流行的事件驅動的python網路框架。因此Scrapy使用了一種非阻塞(又名非同步)的程式碼來實現並行。整體架構大致如下

IO多路複用

# 引擎(EGINE)(大總管)

引擎負責控制系統所有元件之間的資料流,並在某些動作發生時觸發事件。有關詳細資訊,請參見上面的資料流部分。

# 排程器(SCHEDULER)

用來接受引擎發過來的請求, 壓入佇列中, 並在引擎再次請求的時候返回. 可以想像成一個URL的優先順序佇列, 由它來決定下一個要抓取的網址是什麼, 同時去除重複的網址

# 下載器(DOWLOADER)

用於下載網頁內容, 並將網頁內容返回給EGINE,下載器是建立在twisted這個高效的非同步模型上的

# 爬蟲(SPIDERS)

SPIDERS是開發人員自定義的類,用來解析responses,並且提取items,或者傳送新的請求

# 專案管道(ITEM PIPLINES)

在items被提取後負責處理它們,主要包括清理、驗證、持久化(比如存到資料庫)等操作

# 兩個中介軟體

-爬蟲中介軟體

-下載中介軟體(用的最多,加頭,加代理,加cookie,整合selenium)

二、安裝建立和啟動

# 1 框架 不是 模組
# 2 號稱爬蟲界的django(你會發現,跟django很多地方一樣)
# 3 安裝
	-mac,linux平臺:pip3 install scrapy
  -windows平臺:pip3 install scrapy(大部分人可以)
  	- 如果失敗:
      1、pip3 install wheel #安裝後,便支援通過wheel檔案安裝軟體,wheel檔案官網:https://www.lfd.uci.edu/~gohlke/pythonlibs
      3、pip3 install lxml
      4、pip3 install pyopenssl
      5、下載並安裝pywin32:https://sourceforge.net/projects/pywin32/files/pywin32/
      6、下載twisted的wheel檔案:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
      7、執行pip3 install 下載目錄Twisted-17.9.0-cp36-cp36m-win_amd64.whl
      8、pip3 install scrapy
 # 4 在script資料夾下會有scrapy.exe可執行檔案
	-建立scrapy專案:scrapy startproject 專案名   (django建立專案)
  	-建立爬蟲:scrapy genspider 爬蟲名 要爬取的網站地址   # 可以建立多個爬蟲
 # 5 命令啟動爬蟲
		-scrapy crawl 爬蟲名字
  		-scrapy crawl 爬蟲名字 --nolog   # 沒有紀錄檔輸出啟動
 # 6 檔案執行爬蟲(推薦使用)
	-在專案路徑下建立一個main.py,右鍵執行即可
  	from scrapy.cmdline import execute
    # execute(['scrapy','crawl','chouti','--nolog'])  # 沒有設定紀錄檔級別
    execute(['scrapy','crawl','chouti'])			  # 設定了紀錄檔級別

三、組態檔目錄介紹

-crawl_chouti   # 專案名
  -crawl_chouti # 跟專案一個名,資料夾
    -spiders    # spiders:放著爬蟲  genspider生成的爬蟲,都放在這下面
    	-__init__.py
      -chouti.py # 抽屜爬蟲
      -cnblogs.py # cnblogs 爬蟲
    -items.py     # 對比django中的models.py檔案 ,寫一個個的模型類
    -middlewares.py  # 中介軟體(爬蟲中介軟體,下載中介軟體),中介軟體寫在這
    -pipelines.py   # 寫持久化的地方(持久化到檔案,mysql,redis,mongodb)
    -settings.py    # 組態檔
  -scrapy.cfg       # 不用關注,上線相關的
# 組態檔settings.py
ROBOTSTXT_OBEY = False   # 是否遵循爬蟲協定,強行執行
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36'    # 請求頭中的ua,去瀏覽器複製,或者用ua池拿
LOG_LEVEL='ERROR' # 這樣設定,程式錯誤資訊才會列印,
	#啟動爬蟲直接 scrapy crawl 爬蟲名   就沒有紀錄檔輸出
  	# scrapy crawl 爬蟲名 --nolog  # 設定了就不需要這樣啟動了
# 爬蟲檔案
class ChoutiSpider(scrapy.Spider):
    name = 'chouti'   # 爬蟲名字
    allowed_domains = ['https://dig.chouti.com/']  # 允許爬取的域,想要多爬就註釋掉
    start_urls = ['https://dig.chouti.com/']   # 起始爬取的位置,爬蟲一啟動,會先向它發請求
    def parse(self, response):  # 解析,請求回來,自動執行parser,在這個方法中做解析
        print('---------------------------',response)

四、爬取資料,並解析

# 1 解析,可以使用bs4解析
from bs4 import BeautifulSoup
soup=BeautifulSoup(response.text,'lxml')
soup.find_all()  # bs4解析
soup.select()  # css解析
# 2 內建的解析器
response.css  
response.xpath
# 內建解析 
  # 所有用css或者xpath選擇出來的都放在列表中
  # 取第一個:extract_first()
  # 取出所有extract()
# css選擇器取文字和屬性:
    # .link-title::text  # 取文字,資料都在data中
    # .link-title::attr(href)   # 取屬性,資料都在data中
# xpath選擇器取文字和屬性
    # .//a[contains(@class,"link-title")/text()]
    #.//a[contains(@class,"link-title")/@href]
# 內建css選擇期,取所有
div_list = response.css('.link-con .link-item')
for div in div_list:
    content = div.css('.link-title').extract()
    print(content)

五、資料持久化

# 方式一(不推薦)
  -1 parser解析函數,return 列表,列表套字典
    # 命令   (支援:('json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle')
    # 資料到aa.json檔案中
  -2 scrapy crawl chouti -o aa.json   
# 程式碼:
lis = []
for div in div_list:
    content = div.select('.link-title')[0].text
    lis.append({'title':content})
    return lis
# 方式二 pipline的方式(管道)
   -1 在items.py中建立模型類
   -2 在爬蟲中chouti.py,引入,把解析的資料放到item物件中(要用中括號)
   -3 yield item物件
   -4 組態檔設定管道
       ITEM_PIPELINES = {
        # 數位表示優先順序(數位越小,優先順序越大)
       'crawl_chouti.pipelines.CrawlChoutiPipeline': 300,
       'crawl_chouti.pipelines.CrawlChoutiRedisPipeline': 301,
    	}
  -5 pipline.py中寫持久化的類
        spider_open  # 方法,一開始就開啟檔案
        process_item # 方法,寫入檔案
        spider_close # 方法,關閉檔案

儲存到檔案

# choutiaa.py 爬蟲檔案
import scrapy
from chouti.items import ChoutiItem  # 匯入模型類
class ChoutiaaSpider(scrapy.Spider):
    name = 'choutiaa'
    # allowed_domains = ['https://dig.chouti.com/']   # 允許爬取的域
    start_urls = ['https://dig.chouti.com//']   # 起始爬取位置
    # 解析,請求回來,自動執行parse,在這個方法中解析
    def parse(self, response):
        print('----------------',response)
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(response.text,'lxml')
        div_list = soup.select('.link-con .link-item')
        for div in div_list:
            content = div.select('.link-title')[0].text
            href = div.select('.link-title')[0].attrs['href']
            item = ChoutiItem()  # 生成模型物件
            item['content'] = content  # 新增值
            item['href'] = href
            yield item  # 必須用yield  	
# items.py 模型類檔案
import scrapy
class ChoutiItem(scrapy.Item):
    content = scrapy.Field()
    href = scrapy.Field()
# pipelines.py 資料持久化檔案
class ChoutiPipeline(object):
    def open_spider(self, spider):
        # 一開始就開啟檔案
        self.f = open('a.txt', 'w', encoding='utf-8')
    def process_item(self, item, spider):
        # print(item)
        # 寫入檔案的操作
        self.f.write(item['content'])
        self.f.write(item['href'])
        self.f.write('n')
        return item
    def close_spider(self, spider):
        # 寫入完畢,最後關閉檔案
        self.f.close()
# setting.py
ITEM_PIPELINES = {
    # 數位表示優先順序,越小優先順序越高
   'chouti.pipelines.ChoutiPipeline': 300,
   'chouti.pipelines.ChoutiRedisPipeline': 301,
}

儲存到redis

# settings.ps
ITEM_PIPELINES = {
    # 數位表示優先順序,越小優先順序越高
   'chouti.pipelines.ChoutiPipeline': 300,
   'chouti.pipelines.ChoutiRedisPipeline': 301,
}
# pipelines.py
# 儲存到redis
from redis import Redis
class ChoutiRedisPipeline(object):
    def open_spider(self, spider):
        # 不寫引數就用預設設定
        self.conn = Redis(password='123')  # 一開始就拿到redis物件
    def process_item(self, item, spider):
        print(item)
        import json
        s = json.dumps({'content': item['content'], 'href': item['href']})
        self.conn.hset('choudi_article', item['id'], s)
        return item
    def close_spider(self, spoder):
        pass
        # self.conn.close()
# chouti.py
import scrapy
from chouti.items import ChoutiItem  # 匯入模型類
class ChoutiaaSpider(scrapy.Spider):
    name = 'choutiaa'
    # allowed_domains = ['https://dig.chouti.com/']   # 允許爬取的域
    start_urls = ['https://dig.chouti.com//']   # 起始爬取位置
    # 解析,請求回來,自動執行parse,在這個方法中解析
    def parse(self, response):
        print('----------------',response)
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(response.text,'lxml')
        div_list = soup.select('.link-con .link-item')
        for div in div_list:
            content = div.select('.link-title')[0].text
            href = div.select('.link-title')[0].attrs['href']
            id = div.attrs['data-id']
            item = ChoutiItem()  # 生成模型物件
            item['content'] = content  # 新增值
            item['href'] = href
            item['id'] = id
            yield item  # 必須用yield

儲存到MongoDB

#一.下載並安裝mongodb
pip install pymongo
#二、在settings中開啟PIPELINES並把資料庫相應設定寫入
ITEM_PIPELINES = {
    '<spider_name>.pipelines.ChoutiPipeline': 300,
}
MONGODB_HOST = '127.0.0.1'
# 埠號,預設27017
MONGODB_PORT = 27017
# 設定資料庫名稱
MONGODB_DBNAME = 'Chouti'
# 存放本資料的表名稱
MONGODB_DOCNAME = 'Chouti'
#三.修改pipelines檔案
import pymongo
from scrapy.utils.project import get_project_settings
settings = get_project_settings()
class DouluodaluPipeline(object):
    def __init__(self):
        # 獲取setting主機名、埠號和資料庫名稱
        host = settings['MONGODB_HOST']
        port = settings['MONGODB_PORT']
        dbname = settings['MONGODB_DBNAME']
        # 建立資料庫連線
        client = pymongo.MongoClient(host=host,port=port)
        # 指向指定資料庫
        mdb = client[dbname]
        # 獲取資料庫裡面存放資料的表名
        self.post = mdb[settings['MONGODB_DOCNAME']]
    def process_item(self, item, spider):
        data = dict(item)
        # 向指定的表裡新增資料
        self.post.insert(data)
        return item

儲存到mysql

import pymysql.cursors
class MySQLPipeline(object):
    def __init__(self):
        # 連線資料庫
        self.connect = pymysql.connect(
            host='127.0.0.1',  # 資料庫地址
            port=3306,  # 資料庫埠
            db='scrapyMysql',  # 資料庫名
            user='root',  # 資料庫使用者名稱
            passwd='root',  # 資料庫密碼
            charset='utf8',  # 編碼方式
            use_unicode=True)
        # 通過cursor執行增刪查改
        self.cursor = self.connect.cursor()
    def process_item(self, item, spider):
        self.cursor.execute(
            """insert into mingyan(tag, cont)
            value (%s, %s)""",  # 純屬python操作mysql知識,不熟悉請惡補
            (item['tag'],  # item裡面定義的欄位和表欄位對應
             item['cont'],))
        # 提交sql語句
        self.connect.commit()
        return item  # 必須實現返回

六、動作鏈,控制滑動的驗證碼

from selenium import webdriver
from selenium.webdriver import ActionChains
import time
bro=webdriver.Chrome(executable_path='./chromedriver')
bro.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
bro.implicitly_wait(10)
#切換frame(很少)
bro.switch_to.frame('iframeResult')
div=bro.find_element_by_xpath('//*[@id="draggable"]')
# 1 生成一個動作練物件
action=ActionChains(bro)
# 2 點選並夯住某個控制元件
action.click_and_hold(div)
# 3 移動(三種方式)
# action.move_by_offset() # 通過座標(x,y)
# action.move_to_element() # 到另一個標籤
# action.move_to_element_with_offset() # 到另一個標籤,再偏移一部分
for i in range(5):
    action.move_by_offset(10,10)
# 4 真正的移動
action.perform()

# 5 釋放控制元件(鬆開滑鼠)
action.release()

async def login():
    for res in setting.user:
        try:
            username = res[0]
            password = res[1]
            # headless引數設為False,則變成有頭模式
            browser = await launch(
                {'headless': False}
            )
            # 開啟一個頁面
            page = await browser.newPage()
            await page.setViewport(viewport={'width': 1280, 'height': 800})
            res = await page.goto('https://login.taobao.com/', options={'timeout': 10000})
            await page.type('#fm-login-id', username)
            await page.type('#fm-login-password', password)
            await page.waitFor(1000)  # 等待時間
            slider = await page.querySelector('#nc_1_n1z')  # 是否有滾軸
            if slider:
                try:
                    print('有滾軸')
                    await page.hover('#nc_1_n1z')  # 不同場景的驗證碼模組能名字不同。
                    await page.mouse.down()
                    await page.mouse.move(2000, 0, {'delay': random.randint(1000, 2000)})
                    await page.mouse.up()
                except Exception as e:
                    print(e)
                    input('驗證失敗,人工登入:')
            else:
                print('沒有滾軸')
            await page.click("#login-form > div.fm-btn > button")  # 點選登入
            input('進入登入成功頁面後,按回車:')
            return page
        except Exception as e:
            continue

七、提高爬取效率

- 在組態檔中進行相關的設定即可:(預設還有一套setting)
#1 增加並行:
預設scrapy開啟的並行執行緒為32個,可以適當進行增加。在settings組態檔中修改CONCURRENT_REQUESTS = 100值為100,並行設定成了為100。
#2 提高紀錄檔級別:
在執行scrapy時,會有大量紀錄檔資訊的輸出,為了減少CPU的使用率。可以設定log輸出資訊為INFO或者ERROR即可。在組態檔中編寫:LOG_LEVEL = ‘INFO'
# 3 禁止cookie:
如果不是真的需要cookie,則在scrapy爬取資料時可以禁止cookie從而減少CPU的使用率,提升爬取效率。在組態檔中編寫:COOKIES_ENABLED = False
# 4禁止重試:
對失敗的HTTP進行重新請求(重試)會減慢爬取速度,因此可以禁止重試。在組態檔中編寫:RETRY_ENABLED = False
# 5 減少下載超時:
如果對一個非常慢的連結進行爬取,減少下載超時可以能讓卡住的連結快速被放棄,從而提升效率。在組態檔中進行編寫:DOWNLOAD_TIMEOUT = 10 超時時間為10s

八、fake-useragent池

# pip3 install fake-useragent
from fake_useragent import UserAgent
ua = UserAgent(verify_ssl=False)
print(ua.random)  # 隨機獲取一個UserAgent

九、中介軟體設定

#大中介軟體:下載中介軟體,爬蟲中介軟體
# 1 寫在middlewares.py中(名字隨便命名)
# 2 設定生效()
# 爬蟲中介軟體
SPIDER_MIDDLEWARES = {
   'cnblogs_crawl.middlewares.CnblogsCrawlSpiderMiddleware': 543,
}
# 下載中介軟體
DOWNLOADER_MIDDLEWARES = {
   'cnblogs_crawl.middlewares.CnblogsCrawlDownloaderMiddleware': 543,
}
# 下載中介軟體
# 在cnblogs_crawl.middlewares.CnblogsCrawlDownloaderMiddleware中有五個方法
# 請求出去的時候
def process_request(self, request, spider)
	# Must either:
    # - return None:   # 返回none繼續處理,進入下一個中介軟體
    # - return Response: 當次請求結束,把Response丟給引擎處理(可以自己爬,包裝成Response)
    # - return Request : 相當於把Request重新給了引擎,引擎再去做排程
    # - 拋異常:執行process_exception
# 請求回來的時候
def process_response(self, request, response, spider)
	# - return a Response object :繼續處理當次Response,繼續走後續的中介軟體
    # - return a Request object:重新給引擎做排程
	# - 拋異常:執行process_exception
# 請求異常的時候
def process_exception(self, request, exception, spider)
	# - return None: 不處理異常,繼續丟給下面
    # - return a Response:停止例外處理,不丟給下面。給引擎。Response給爬蟲分析資料
    # - return a Request:停止例外處理,不丟給下面。給引擎。Request重新排程
    

process_exception 錯誤處理

class CnblogsSpider(scrapy.Spider):
    name = 'cnblogs4'
    allowed_domains = ['www.cnblogs.com']
    start_urls = ['http://wwwsadasd.cnblogs.com/']   # 錯誤的網址,報錯走例外處理
# 走例外處理,重新返回一個正確的Request物件
def process_exception(self, request, exception, spider):
    print(request.url)  # http://wwwsadasd.cnblogs.com/
    from scrapy.http import Request
    return Request('http://www.cnblogs.com/',callback=spider.parser_detail)

process_request 加代理,加cookie等

    def process_request(self, request, spider):
        # 1 加cookie(request.cookies就是存取該網站的cookie)
        print(request.cookies)
        request.cookies={'name':"jeff",'age':18}  # 從你的cookie池中取出來的,  字典
        print(request.cookies)
        # 2 加代理
        request.meta['proxy']=self.get_proxy()   # 從代理池中獲取一個
        print(request.meta['proxy'])
        # 3 修改ua
        from fake_useragent import UserAgent   # ua模組,隨機獲取一個
        ua = UserAgent(verify_ssl=False)
        request.headers['User-Agent']=ua.random
        print(request.headers)
# 代理池
def get_proxy(self):
    import requests
    ret=requests.get('http://0.0.0.0:5010/get').json()['proxy']
    print(ret)
    return ret
        return None

十、整合selenium

#可在兩個地方整合。
#1.process_request(請求出去的時候)  # 推薦寫這裡,少請求一次。直接整合封裝
#2.process_response(請求回來的時候) # 不推薦,因為奪走了一次請求,回來再整合封裝
# 方案一:缺點很大。每次一請求都要開啟一個bro瀏覽器
def process_request(self, request, spider):
    from selenium import webdriver
    from scrapy.http import HtmlResponse
    bro = webdriver.Chrome(executable_path='../chromedriver')
    bro.get(request.url)
    text = bro.page_source
    response = HtmlResponse(url=request.url, body=text.encode('utf-8'), status=200)
    return response
# 方案二:改進為一開始就開啟一個bro瀏覽器,後面都用這一個bro
class CnblogsSpider(scrapy.Spider):
    name = 'cnblogs'
    from selenium import webdriver
    # 在爬蟲一開始就開啟bro物件
    bro = webdriver.Chrome(executable_path='../chromedriver')  
    # 在爬蟲中新新增的方法:關閉bro
    def close(spider, reason):
    	spider.bro.close()  # 爬蟲結束關閉
# 中介軟體中
def process_request(self, request, spider):
    from scrapy.http import HtmlResponse
    spider.bro.get(request.url)  # 每個請求使用一個bro
    text = spider.bro.page_source
    response = HtmlResponse(url=request.url, body=text.encode('utf-8'), status=200)
    return response

十一、指紋和布隆過濾器實現增量爬取

什麼是增量爬取?

-增量爬取(100連結,150個連結)

  • -已經爬過的,放到某個位置(mysql,redis中:集合)
  • -如果用預設的,爬過的地址,放在記憶體中,只要專案一重啟,就沒了,它也不知道我爬過那個了,所以要自己重寫去重方案

-你寫的去重方案,佔得記憶體空間更小

    -bitmap方案

    -BloomFilter布隆過濾器

網址指紋

# 一、網址指紋
from scrapy.http import Request
from scrapy.utils.request import request_fingerprint
# 這種網址是一個
request1 = Request(url='https://www.baidu.com/s?name=jeff&age=18')
request2 = Request(url='https://www.baidu.com/s?age=18&name=jeff')
ret1=request_fingerprint(requests1)
ret2=request_fingerprint(requests2)
print(ret1) # 6961985868392ae44c15ada494ddeda856cf75fc
print(ret2) # 6961985868392ae44c15ada494ddeda856cf75fc

布隆過濾器

# 安裝
# 1.需要先安裝bitarray  #下載地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/
# 2.下載好之後 pip3 install 檔案拖進去
# 3.pip3 install pybloom_live
#ScalableBloomFilter 可以自動擴容
from pybloom_live import ScalableBloomFilter
bloom = ScalableBloomFilter(initial_capacity=100, error_rate=0.001, mode=ScalableBloomFilter.LARGE_SET_GROWTH)
url = "https://www.baidu.com/s?name=jeff&age=18"
url2 = "https://www.baidu.com/s?age=18&name=jeff"
bloom.add(url)
print(url in bloom)
print(url2 in bloom)

使用一:新增網址(不推薦)

#BloomFilter 是定長的
from pybloom_live import BloomFilter
bf = BloomFilter(capacity=1000)
url='www.baidu.com'
bf.add(url)
print(url in bf)
print("www.liuqingzheng.top" in bf)

使用二:新增網址指紋(推薦),配合指紋使用

from scrapy.http import Request
from scrapy.utils.request import request_fingerprint
from pybloom_live import BloomFilter
request1 = Request(url='https://www.baidu.com/s?name=jeff&age=18')
request2 = Request(url='https://www.baidu.com/s?age=18&name=jeff')
ret1=request_fingerprint(request1)
ret2=request_fingerprint(request2)
print(ret1) # 6961985868392ae44c15ada494ddeda856cf75fc
print(ret2) # 6961985868392ae44c15ada494ddeda856cf75fc
bf = BloomFilter(capacity=1000) # 1000容量
bf.add(ret2)
if ret1 in bf:
    print('已經爬過此網站,True')
else:
    bf.add(ret1)  # 新增
    print('還沒有爬過此網站,返回false')

十二、分散式爬蟲

github地址:https://github.com/rmax/scrapy-redis
# 1 安裝pip3 install scrapy-redis
# 原始碼部分,不到1000行,
# 1 原來的爬蟲繼承
from scrapy_redis.spiders import RedisSpider
class CnblogsSpider(RedisSpider):
  	#start_urls = ['http://www.cnblogs.com/']
    redis_key = 'myspider:start_urls'  # 起始地址為空,在redis中拿
# 2 在setting中設定
  SCHEDULER = "scrapy_redis.scheduler.Scheduler"
  DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
  ITEM_PIPELINES = { 
     'chouti.pipelines.Pipeline': 300,               # 用自己的入庫類,比如mysql中
     # 'scrapy_redis.pipelines.RedisPipeline': 300  # 存在別人寫好的redis入庫類
  }
REDIS_PARAMS  = {'password':'123'}   # 如果redis有密碼就設定
#其他更多設定見github
# 3 多臺機器上啟動scrapy
# 4 向reids中傳送起始url
redis-cli lpush myspider:start_urls https://www.cnblogs.com

十三、爬蟲框架全站爬取使用案例

可以同時啟動兩個爬蟲,爬不同的網站。但是建議爬不同的網站新建專案

chouti.py 爬蟲:

import scrapy
from chouti.items import ChoutiItem  # 匯入模型類
class ChoutiaaSpider(scrapy.Spider):
    name = 'choutiaa'
    # allowed_domains = ['https://dig.chouti.com/']   # 允許爬取的域
    start_urls = ['https://dig.chouti.com//']   # 起始爬取位置
    # 解析,請求回來,自動執行parse,在這個方法中解析
    def parse(self, response):
        print('----------------',response)
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(response.text,'lxml')
        div_list = soup.select('.link-con .link-item')
        for div in div_list:
            content = div.select('.link-title')[0].text
            href = div.select('.link-title')[0].attrs['href']
            id = div.attrs['data-id']
            item = ChoutiItem()  # 生成模型物件
            item['content'] = content  # 新增值
            item['href'] = href
            item['id'] = id
            yield item  # 必須用yield

cnblogs.py 爬蟲:

# -*- coding: utf-8 -*-
import scrapy
from bs4 import BeautifulSoup
from chouti.items import CnblogsItem  # 匯入模型類
from scrapy.http import Request
class CnblogsSpider(scrapy.Spider):
    name = 'cnblogs'
    start_urls = ['https://www.cnblogs.com/']
    def parse(self, response):
        print('------', response)
        soup = BeautifulSoup(response.text, 'lxml')
        div_list = soup.select('#post_list .post_item')
        for div in div_list:
            author = div.select('.post_item_foot a')[0].text
            content_url = div.select('h3 a')[0].attrs['href']
            title = div.select('h3')[0].text
            content_summary = div.select('p')[0].text
            item = CnblogsItem()
            item['author'] = author
            item['content_url'] = content_url
            item['title'] = title
            item['content_summary'] = content_summary
            # print(f'''
            # 作者:{author}
            # 文章地址:{content_url}
            # 標題:{title}
            # 文章內容:{content_summary}
            # ''')            
            # 繼續往深一層爬取,傳遞給content_parse
            yield Request(content_url, callback=self.content_parse, meta={'item': item})
        # 獲取下一頁的標籤網址
        next = soup.select('#paging_block > div > a:nth-last-child(1)')[0].attrs['href']
        next = 'https://www.cnblogs.com/'+next
        yield Request(next)   # 繼續爬取下一頁
    def content_parse(self, response):
        item = response.meta.get('item')
        content = response.css('#cnblogs_post_body').extract_first()
        if not content:
            content = response.css('content').extract_first()
        item['content'] = content
        # print(item)
        yield item

items.py 模型類:

# -*- coding: utf-8 -*-
# Define here the models for your scraped items
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class ChoutiItem(scrapy.Item):
    content = scrapy.Field()
    href = scrapy.Field()
    id = scrapy.Field()
class CnblogsItem(scrapy.Item):
    author = scrapy.Field()
    content_url = scrapy.Field()
    title = scrapy.Field()
    content_summary = scrapy.Field()
    content = scrapy.Field()

pipelines.py 資料持久化檔案

# -*- coding: utf-8 -*-
# Define your item pipelines here
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
# 儲存到檔案
class Pipeline(object):
    def open_spider(self, spider):
        # choutiaa爬蟲入庫前
        if spider.name == 'choutiaa':
            # 一開始就開啟檔案
            self.f = open('a.txt', 'w', encoding='utf-8')
        # cnblog爬蟲入庫前
        elif spider.name == 'cnblogs':
            import pymysql
            self.conn = pymysql.Connect(host='127.0.0.1', port=3306, db='cnblogs', user='root', password="123",autocommit=True)
    def process_item(self, item, spider):
        # choutiaa爬蟲入庫中
        if spider.name == 'choutiaa':
            # 寫入檔案的操作
            self.f.write(item['content'])
            self.f.write(item['href'])
            self.f.write(item['id'])
            self.f.write('n')
            return item
        # cnblog爬蟲入庫中
        elif spider.name == 'cnblogs':
            print('cnblogs入庫中')
            curser = self.conn.cursor()
            sql = 'insert into article (author,content_url,title,content_summary,content) values (%s,%s,%s,%s,%s)'
            curser.execute(sql, (
            item['author'], item['content_url'], item['title'], item['content_summary'], item['content']))
    def close_spider(self, spider):
        # choutiaa爬蟲入庫結束
        if spider.name == 'choutiaa':
            # 寫入完畢,最後關閉檔案
            self.f.close()
        # cnblog爬蟲入庫結束
        elif spider.name == 'cnblogs':
            print('cnblogs入庫完畢')
            self.conn.close()

main.py

from scrapy.cmdline import execute
# execute(['scrapy','crawl','choutiaa'])
execute(['scrapy','crawl','cnblogs'])

以上就是scarpy爬蟲框架整合selenium及詳細講解的詳細內容,更多關於scarpy爬蟲框架結構整合selenium的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com