Scrapy爬蟲框架集成selenium及全面詳細(xì)講解
一、架構(gòu)介紹
Scrapy一個開源和協(xié)作的框架,其最初是為了頁面抓取 (更確切來說, 網(wǎng)絡(luò)抓取 )所設(shè)計的,使用它可以以快速、簡單、可擴展的方式從網(wǎng)站中提取所需的數(shù)據(jù)。但目前Scrapy的用途十分廣泛,可用于如數(shù)據(jù)挖掘、監(jiān)測和自動化測試等領(lǐng)域,也可以應(yīng)用在獲取API所返回的數(shù)據(jù)(例如 Amazon Associates Web Services ) 或者通用的網(wǎng)絡(luò)爬蟲。
Scrapy 是基于twisted框架開發(fā)而來,twisted是一個流行的事件驅(qū)動的python網(wǎng)絡(luò)框架。因此Scrapy使用了一種非阻塞(又名異步)的代碼來實現(xiàn)并發(fā)。整體架構(gòu)大致如下
IO多路復(fù)用
# 引擎(EGINE)(大總管)
引擎負(fù)責(zé)控制系統(tǒng)所有組件之間的數(shù)據(jù)流,并在某些動作發(fā)生時觸發(fā)事件。有關(guān)詳細(xì)信息,請參見上面的數(shù)據(jù)流部分。
# 調(diào)度器(SCHEDULER)
用來接受引擎發(fā)過來的請求, 壓入隊列中, 并在引擎再次請求的時候返回. 可以想像成一個URL的優(yōu)先級隊列, 由它來決定下一個要抓取的網(wǎng)址是什么, 同時去除重復(fù)的網(wǎng)址
# 下載器(DOWLOADER)
用于下載網(wǎng)頁內(nèi)容, 并將網(wǎng)頁內(nèi)容返回給EGINE,下載器是建立在twisted這個高效的異步模型上的
# 爬蟲(SPIDERS)
SPIDERS是開發(fā)人員自定義的類,用來解析responses,并且提取items,或者發(fā)送新的請求
# 項目管道(ITEM PIPLINES)
在items被提取后負(fù)責(zé)處理它們,主要包括清理、驗證、持久化(比如存到數(shù)據(jù)庫)等操作
# 兩個中間件
-爬蟲中間件
-下載中間件(用的最多,加頭,加代理,加cookie,集成selenium)
二、安裝創(chuàng)建和啟動
# 1 框架 不是 模塊 # 2 號稱爬蟲界的django(你會發(fā)現(xiàn),跟django很多地方一樣) # 3 安裝 -mac,linux平臺:pip3 install scrapy -windows平臺:pip3 install scrapy(大部分人可以) - 如果失敗: 1、pip3 install wheel #安裝后,便支持通過wheel文件安裝軟件,wheel文件官網(wǎng):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、執(zhí)行pip3 install 下載目錄\Twisted-17.9.0-cp36-cp36m-win_amd64.whl 8、pip3 install scrapy # 4 在script文件夾下會有scrapy.exe可執(zhí)行文件 -創(chuàng)建scrapy項目:scrapy startproject 項目名 (django創(chuàng)建項目) -創(chuàng)建爬蟲:scrapy genspider 爬蟲名 要爬取的網(wǎng)站地址 # 可以創(chuàng)建多個爬蟲 # 5 命令啟動爬蟲 -scrapy crawl 爬蟲名字 -scrapy crawl 爬蟲名字 --nolog # 沒有日志輸出啟動 # 6 文件執(zhí)行爬蟲(推薦使用) -在項目路徑下創(chuàng)建一個main.py,右鍵執(zhí)行即可 from scrapy.cmdline import execute # execute(['scrapy','crawl','chouti','--nolog']) # 沒有設(shè)置日志級別 execute(['scrapy','crawl','chouti']) # 設(shè)置了日志級別
三、配置文件目錄介紹
-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 # 不用關(guān)注,上線相關(guān)的 # 配置文件settings.py ROBOTSTXT_OBEY = False # 是否遵循爬蟲協(xié)議,強行運行 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,去瀏覽器復(fù)制,或者用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/'] # 起始爬取的位置,爬蟲一啟動,會先向它發(fā)請求 def parse(self, response): # 解析,請求回來,自動執(zhí)行parser,在這個方法中做解析 print('---------------------------',response)
四、爬取數(shù)據(jù),并解析
# 1 解析,可以使用bs4解析 from bs4 import BeautifulSoup soup=BeautifulSoup(response.text,'lxml') soup.find_all() # bs4解析 soup.select() # css解析 # 2 內(nèi)置的解析器 response.css response.xpath # 內(nèi)置解析 # 所有用css或者xpath選擇出來的都放在列表中 # 取第一個:extract_first() # 取出所有extract() # css選擇器取文本和屬性: # .link-title::text # 取文本,數(shù)據(jù)都在data中 # .link-title::attr(href) # 取屬性,數(shù)據(jù)都在data中 # xpath選擇器取文本和屬性 # .//a[contains(@class,"link-title")/text()] #.//a[contains(@class,"link-title")/@href] # 內(nèi)置css選擇期,取所有 div_list = response.css('.link-con .link-item') for div in div_list: content = div.css('.link-title').extract() print(content)
五、數(shù)據(jù)持久化
# 方式一(不推薦) -1 parser解析函數(shù),return 列表,列表套字典 # 命令 (支持:('json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle') # 數(shù)據(jù)到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中創(chuàng)建模型類 -2 在爬蟲中chouti.py,引入,把解析的數(shù)據(jù)放到item對象中(要用中括號) -3 yield item對象 -4 配置文件配置管道 ITEM_PIPELINES = { # 數(shù)字表示優(yōu)先級(數(shù)字越小,優(yōu)先級越大) 'crawl_chouti.pipelines.CrawlChoutiPipeline': 300, 'crawl_chouti.pipelines.CrawlChoutiRedisPipeline': 301, } -5 pipline.py中寫持久化的類 spider_open # 方法,一開始就打開文件 process_item # 方法,寫入文件 spider_close # 方法,關(guān)閉文件
保存到文件
# choutiaa.py 爬蟲文件 import scrapy from chouti.items import ChoutiItem # 導(dǎo)入模型類 class ChoutiaaSpider(scrapy.Spider): name = 'choutiaa' # allowed_domains = ['https://dig.chouti.com/'] # 允許爬取的域 start_urls = ['https://dig.chouti.com//'] # 起始爬取位置 # 解析,請求回來,自動執(zhí)行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 數(shù)據(jù)持久化文件 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): # 寫入完畢,最后關(guān)閉文件 self.f.close() # setting.py ITEM_PIPELINES = { # 數(shù)字表示優(yōu)先級,越小優(yōu)先級越高 'chouti.pipelines.ChoutiPipeline': 300, 'chouti.pipelines.ChoutiRedisPipeline': 301, }
保存到redis
# settings.ps ITEM_PIPELINES = { # 數(shù)字表示優(yōu)先級,越小優(yōu)先級越高 'chouti.pipelines.ChoutiPipeline': 300, 'chouti.pipelines.ChoutiRedisPipeline': 301, } # pipelines.py # 保存到redis from redis import Redis class ChoutiRedisPipeline(object): def open_spider(self, spider): # 不寫參數(shù)就用默認(rèn)配置 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 # 導(dǎo)入模型類 class ChoutiaaSpider(scrapy.Spider): name = 'choutiaa' # allowed_domains = ['https://dig.chouti.com/'] # 允許爬取的域 start_urls = ['https://dig.chouti.com//'] # 起始爬取位置 # 解析,請求回來,自動執(zhí)行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并把數(shù)據(jù)庫相應(yīng)配置寫入 ITEM_PIPELINES = { '<spider_name>.pipelines.ChoutiPipeline': 300, } MONGODB_HOST = '127.0.0.1' # 端口號,默認(rèn)27017 MONGODB_PORT = 27017 # 設(shè)置數(shù)據(jù)庫名稱 MONGODB_DBNAME = 'Chouti' # 存放本數(shù)據(jù)的表名稱 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主機名、端口號和數(shù)據(jù)庫名稱 host = settings['MONGODB_HOST'] port = settings['MONGODB_PORT'] dbname = settings['MONGODB_DBNAME'] # 創(chuàng)建數(shù)據(jù)庫連接 client = pymongo.MongoClient(host=host,port=port) # 指向指定數(shù)據(jù)庫 mdb = client[dbname] # 獲取數(shù)據(jù)庫里面存放數(shù)據(jù)的表名 self.post = mdb[settings['MONGODB_DOCNAME']] def process_item(self, item, spider): data = dict(item) # 向指定的表里添加數(shù)據(jù) self.post.insert(data) return item
保存到mysql
import pymysql.cursors class MySQLPipeline(object): def __init__(self): # 連接數(shù)據(jù)庫 self.connect = pymysql.connect( host='127.0.0.1', # 數(shù)據(jù)庫地址 port=3306, # 數(shù)據(jù)庫端口 db='scrapyMysql', # 數(shù)據(jù)庫名 user='root', # 數(shù)據(jù)庫用戶名 passwd='root', # 數(shù)據(jù)庫密碼 charset='utf8', # 編碼方式 use_unicode=True) # 通過cursor執(zhí)行增刪查改 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里面定義的字段和表字段對應(yīng) item['cont'],)) # 提交sql語句 self.connect.commit() return item # 必須實現(xiàn)返回
六、動作鏈,控制滑動的驗證碼
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() # 通過坐標(biāo)(x,y) # action.move_to_element() # 到另一個標(biāo)簽 # action.move_to_element_with_offset() # 到另一個標(biāo)簽,再偏移一部分 for i in range(5): action.move_by_offset(10,10) # 4 真正的移動 action.perform() # 5 釋放控件(松開鼠標(biāo)) action.release() async def login(): for res in setting.user: try: username = res[0] password = res[1] # headless參數(shù)設(shè)為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
七、提高爬取效率
- 在配置文件中進行相關(guān)的配置即可:(默認(rèn)還有一套setting) #1 增加并發(fā): 默認(rèn)scrapy開啟的并發(fā)線程為32個,可以適當(dāng)進行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值為100,并發(fā)設(shè)置成了為100。 #2 提高日志級別: 在運行scrapy時,會有大量日志信息的輸出,為了減少CPU的使用率??梢栽O(shè)置log輸出信息為INFO或者ERROR即可。在配置文件中編寫:LOG_LEVEL = ‘INFO' # 3 禁止cookie: 如果不是真的需要cookie,則在scrapy爬取數(shù)據(jù)時可以禁止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繼續(xù)處理,進入下一個中間件 # - return Response: 當(dāng)次請求結(jié)束,把Response丟給引擎處理(可以自己爬,包裝成Response) # - return Request : 相當(dāng)于把Request重新給了引擎,引擎再去做調(diào)度 # - 拋異常:執(zhí)行process_exception # 請求回來的時候 def process_response(self, request, response, spider) # - return a Response object :繼續(xù)處理當(dāng)次Response,繼續(xù)走后續(xù)的中間件 # - return a Request object:重新給引擎做調(diào)度 # - 拋異常:執(zhí)行process_exception # 請求異常的時候 def process_exception(self, request, exception, spider) # - return None: 不處理異常,繼續(xù)丟給下面 # - return a Response:停止異常處理,不丟給下面。給引擎。Response給爬蟲分析數(shù)據(jù) # - return a Request:停止異常處理,不丟給下面。給引擎。Request重新調(diào)度
process_exception 錯誤處理
class CnblogsSpider(scrapy.Spider): name = 'cnblogs4' allowed_domains = ['www.cnblogs.com'] start_urls = ['http://wwwsadasd.cnblogs.com/'] # 錯誤的網(wǎng)址,報錯走異常處理 # 走異常處理,重新返回一個正確的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就是訪問該網(wǎng)站的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') # 在爬蟲中新添加的方法:關(guān)閉bro def close(spider, reason): spider.bro.close() # 爬蟲結(jié)束關(guān)閉 # 中間件中 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
十一、指紋和布隆過濾器實現(xiàn)增量爬取
什么是增量爬???
-增量爬?。?00鏈接,150個鏈接)
- -已經(jīng)爬過的,放到某個位置(mysql,redis中:集合)
- -如果用默認(rèn)的,爬過的地址,放在內(nèi)存中,只要項目一重啟,就沒了,它也不知道我爬過那個了,所以要自己重寫去重方案
-你寫的去重方案,占得內(nèi)存空間更小
-bitmap方案
-BloomFilter布隆過濾器
網(wǎng)址指紋
# 一、網(wǎng)址指紋 from scrapy.http import Request from scrapy.utils.request import request_fingerprint # 這種網(wǎng)址是一個 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)
使用一:添加網(wǎng)址(不推薦)
#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)
使用二:添加網(wǎng)址指紋(推薦),配合指紋使用
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('已經(jīng)爬過此網(wǎng)站,True') else: bf.add(ret1) # 添加 print('還沒有爬過此網(wǎng)站,返回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中發(fā)送起始url redis-cli lpush myspider:start_urls https://www.cnblogs.com
十三、爬蟲框架全站爬取使用案例
可以同時啟動兩個爬蟲,爬不同的網(wǎng)站。但是建議爬不同的網(wǎng)站新建項目
chouti.py 爬蟲:
import scrapy from chouti.items import ChoutiItem # 導(dǎo)入模型類 class ChoutiaaSpider(scrapy.Spider): name = 'choutiaa' # allowed_domains = ['https://dig.chouti.com/'] # 允許爬取的域 start_urls = ['https://dig.chouti.com//'] # 起始爬取位置 # 解析,請求回來,自動執(zhí)行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 # 導(dǎo)入模型類 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} # 標(biāo)題:{title} # 文章內(nèi)容:{content_summary} # ''') # 繼續(xù)往深一層爬取,傳遞給content_parse yield Request(content_url, callback=self.content_parse, meta={'item': item}) # 獲取下一頁的標(biāo)簽網(wǎng)址 next = soup.select('#paging_block > div > a:nth-last-child(1)')[0].attrs['href'] next = 'https://www.cnblogs.com/'+next yield Request(next) # 繼續(xù)爬取下一頁 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 數(shù)據(jù)持久化文件
# -*- 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爬蟲入庫結(jié)束 if spider.name == 'choutiaa': # 寫入完畢,最后關(guān)閉文件 self.f.close() # cnblog爬蟲入庫結(jié)束 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及詳細(xì)講解的詳細(xì)內(nèi)容,更多關(guān)于scarpy爬蟲框架結(jié)構(gòu)集成selenium的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于DataFrame篩選數(shù)據(jù)與loc的用法詳解
今天小編就為大家分享一篇基于DataFrame篩選數(shù)據(jù)與loc的用法詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-05-05利用python庫在局域網(wǎng)內(nèi)傳輸文件的方法
今天小編就為大家分享一篇利用python庫在局域網(wǎng)內(nèi)傳輸文件的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-06-06