1.介紹:是一個具備不少功能且具備很強通用性的一個項目模板 2.Linux: 直接 pip install scrapy 3.windows: 1.pip install wheel 2.下載twisted https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted 3.進入到下載的目錄 pip install Twisted-19.2.0-cp36-cp36m-win_amd64 4.pip install pywin32 5.pip install scrapy
1.建立項目: scrapy startproject 項目名稱 2.建立爬蟲的應用程序; 進入項目的目錄 scrapy genspider 應用的名稱 爬取網頁的其實url 3.編寫爬蟲文件,在第二步執行完的時候就會在項目的spiders中生成一個應用名的py爬蟲文件 4.修改settings.py配置文件的相關配置 5.執行爬蟲程序在cmd中 scrapy crawl 應用的名稱
1.建立項目: scrapy startproject 項目名稱 project_name/ scrapy.cfg: project_name/ __init__.py items.py pipelines.py settings.py spiders/ __init__.py scrapy.cfg 項目的主配置信息。(真正爬蟲相關的配置信息在settings.py文件中) items.py 設置數據存儲模板,用於結構化數據,如:Django的Model pipelines 數據持久化處理 settings.py 配置文件,如:遞歸的層數、併發數,延遲下載等 spiders 爬蟲目錄,如:建立文件,編寫爬蟲解析規則
2.建立爬蟲的應用程序; 進入項目的目錄 scrapy genspider 應用的名稱 爬取網頁的其實url
3.編寫爬蟲文件,在第二步執行完的時候就會在項目的spiders中生成一個應用名的py爬蟲文件 # -*- coding: utf-8 -*- import scrapy class QiubaiSpider(scrapy.Spider): name = 'qiubai' #應用名稱 #容許爬取的域名(若是遇到非該域名的url則爬取不到數據) allowed_domains = ['https://www.qiushibaike.com/'] #起始爬取的url start_urls = ['https://www.qiushibaike.com/'] #訪問起始URL並獲取結果後的回調函數,該函數的response參數就是向起始的url發送請求後,獲取的響應對象.該函數返回值必須爲可迭代對象或者NUll def parse(self, response): print(response.text) #獲取字符串類型的響應內容 print(response.body)#獲取字節類型的相應內容
4.修改settings.py配置文件的相關配置 19行:USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' #假裝請求載體身份 22行:ROBOTSTXT_OBEY = False #能夠忽略或者不遵照robots協議
5.執行爬蟲程序在cmd中 scrapy crawl 應用的名稱
例子: 爬取站長素材的圖片 在scrapy項目和爬蟲應用建好的狀況下:
from urllib import request import requests import scrapy import os from lxml import etree class ZzmeiziSpider(scrapy.Spider): name = 'zzmeizi' # allowed_domains = ['http://sc.chinaz.com/tag_tupian/YaZhouMeiNv.html'] start_urls = ['http://sc.chinaz.com/tag_tupian/YaZhouMeiNv.html'] def parse(self, response): # print(response.text) # 獲取字符串類型的響應內容 # print(response.body) # 獲取字節類型的相應內容 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36' } tree = etree.HTML(response.body) ret_list = tree.xpath('//*[@id="container"]//div/div/a/@href') print(ret_list) if not os.path.exists('./zzmeizi'): os.mkdir('./zzmeizi') for div in ret_list: page_text = requests.get(url=div, headers=headers).content tree = etree.HTML(page_text) img_name = tree.xpath('//div[@class="imga"]/a/@title')[0] img_url = tree.xpath('//div[@class="imga"]/a/@href')[0] request.urlretrieve(img_url, './zzmeizi/%s.jpg' % (img_name)) // 訪問並保存
全站數據的爬取:手動請求的發送 scrapy默承認以進行cookie的攜帶和處理 處理請求傳參: - 使用場景:爬取解析的數據沒有存在於同一張頁面中 - meta參數 持久化存儲的實現流程(基於管道): - 數據解析 - 封裝item類(將字段先在items文件中封裝一下) - 實例化item類型的對象 - 將解析到的數據依次存儲封裝到item類型的對象中 - 將item提交給管道 - 在管道中實現io操做 - 在settings文件中開啓管道 將一份數據存儲到不一樣的平臺中 - 管道文件中一個管道類負責將item存儲到某一個平臺中 - 配置文件中設定管道類的優先級 - process_item 方法中return item 的操做將item傳遞給下一個即將被執行的管道類
import scrapy class XioahuaproItem(scrapy.Item): # define the fields for your item here like: name = scrapy.Field() img_url = scrapy.Field()
class MysqlPipeline(object): conn = None cursor = None def open_spider(self, spider): #解決數據庫字段沒法存儲中文處理:alter table tableName convert to charset utf8; self.conn = pymysql.Connect(host='127.0.0.1',port=3306,user='root',password='123',db='test') print(self.conn) def process_item(self, item, spider): self.cursor = self.conn.cursor() try: self.cursor.execute('insert into xiahua values ("%s","%s")'%(item['name'],item['img_url'])) self.conn.commit() except Exception as e: print(e) self.conn.rollback() return item def close_spider(self, spider): self.cursor.close() self.conn.close()
import scrapy class PostdemoSpider(scrapy.Spider): name = 'postDemo' # allowed_domains = ['www.xxx.com'] start_urls = ['https://fanyi.baidu.com/sug'] def start_requests(self): data = { 'kw':'dog' } for url in self.start_urls: yield scrapy.FormRequest(url=url,callback=self.parse,formdata=data) def parse(self, response): print(response.text)
當進行深層爬取時須要進行數據的傳遞; class MovieSpider(scrapy.Spider): name = 'movie' # allowed_domains = ['www.xxx.com'] start_urls = ['https://www.4567tv.tv/index.php/vod/show/id/9.html'] #接收一個請求傳遞過來的數據 def detail_parse(self,response): item = response.meta['item'] desc = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[5]/span[2]/text()').extract_first() item['desc'] = desc yield item def parse(self, response): li_list = response.xpath('//div[@class="stui-pannel_bd"]/ul/li') for li in li_list: name = li.xpath('.//h4[@class="title text-overflow"]/a/text()').extract_first() detail_url = 'https://www.4567tv.tv'+li.xpath('.//h4[@class="title text-overflow"]/a/@href').extract_first() item = MovieproItem() item['name'] = name #meta是一個字典,字典中全部的鍵值對均可以傳遞給指定好的回調函數 yield scrapy.Request(url=detail_url,callback=self.detail_parse,meta={'item':item})
增長併發: 默認scrapy開啓的併發線程爲32個,能夠適當進行增長。在settings配置文件中修改CONCURRENT_REQUESTS = 100值爲100,併發設置成了爲100。 下降日誌級別: 在運行scrapy時,會有大量日誌信息的輸出,爲了減小CPU的使用率。能夠設置log輸出信息爲INFO或者ERROR便可。在配置文件中編寫:LOG_LEVEL = ‘INFO’ 禁止cookie: 若是不是真的須要cookie,則在scrapy爬取數據時能夠禁止cookie從而減小CPU的使用率,提高爬取效率。在配置文件中編寫:COOKIES_ENABLED = False 禁止重試: 對失敗的HTTP進行從新請求(重試)會減慢爬取速度,所以能夠禁止重試。在配置文件中編寫:RETRY_ENABLED = False 減小下載超時: 若是對一個很是慢的連接進行爬取,減小下載超時能夠能讓卡住的連接快速被放棄,從而提高效率。在配置文件中進行編寫:DOWNLOAD_TIMEOUT = 10 超時時間爲10s
下載中間件 - 做用:批量攔截請求個響應 - 攔截請求: 1. 篡改請求頭信息(User-Agent): 2.處理異常的請求 3.對響應數據進行修改 1.在scrapy中如何給全部的請求對象儘量多的設置不同的請求載體身份表示 - UA池, process_request(request) 中間件中處理 2.scrapy 中如何給發生異常請求設置代理IP - ip池, process_exception(request,response,spider):request.meta['proxy' ] = 'http://ip:prot' - 講異常的請求攔截到以後,經過代理ip相關的操做,就能夠將異常的請求變爲非異常的請求,對該請求從新發送: return request 3.簡述下載中間件的做用及其最重要三個方法的做用
- process_request:攔截全部非異常的請求 - process_response:攔截全部的響應對象 - process_exception:攔截髮生異常的請求對象,須要對異常的請求對象進行相關處理,讓其變成 正常的請求對象,而後經過return request 對該請求進行從新發送
4.簡述scrapy中何時須要使用selenium及其scrapy應用selenium的編碼流程php
- 實例化瀏覽器對象(一次):spider的init方法 - 須要編寫瀏覽器自動化的操做(中間件的process_response) - 關閉瀏覽器(spider的closed方法中進行關閉操做)
- crawlSpider全站數據爬取 - crawlSpider就是spider一個子類(派生) - crawlSpider具備的機制: - 鏈接提取器 - 規則解析器 - 建立爬蟲文件: - 深度爬取 - 命令 - scrapy genspider -t crawl 應用名 url地址
# 爬蟲文件 import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from bossPro.items import DetailItem,FirstItem #爬取的是崗位名稱(首頁)和崗位描述(詳情頁) class BossSpider(CrawlSpider): name = 'boss' # allowed_domains = ['www.xxx.com'] start_urls = ['https://www.zhipin.com/c101010100/?query=python%E5%BC%80%E5%8F%91&page=1&ka=page-prev'] #獲取全部的頁碼鏈接 link = LinkExtractor(allow=r'page=\d+') link_detail = LinkExtractor(allow=r'/job_detail/.*?html') #/job_detail/f2a47b2f40c53bd41XJ93Nm_GVQ~.html #/job_detail/47dc9803e93701581XN80ty7GFI~.html rules = ( Rule(link, callback='parse_item', follow=True), Rule(link_detail, callback='parse_detail'), ) #將頁碼鏈接對應的頁面數據中的崗位名稱進行解析 def parse_item(self, response): li_list = response.xpath('//div[@class="job-list"]/ul/li') for li in li_list: item = FirstItem() job_title = li.xpath('.//div[@class="job-title"]/text()').extract_first() item['job_title'] = job_title # print(job_title) yield item def parse_detail(self,response): job_desc = response.xpath('//*[@id="main"]/div[3]/div/div[2]/div[2]/div[1]/div//text()').extract() item = DetailItem() job_desc = ''.join(job_desc) item['job_desc'] = job_desc yield item
# item 文件 import scrapy class DetailItem(scrapy.Item): # define the fields for your item here like: job_desc = scrapy.Field() class FirstItem(scrapy.Item): # define the fields for your item here like: job_title = scrapy.Field()
# 管道 持久化文件 class BossproPipeline(object): f1,f2 = None,None def open_spider(self,spider): self.f1 = open('a.txt','w',encoding='utf-8') self.f2 = open('b.txt', 'w', encoding='utf-8') def process_item(self, item, spider): #item在同一時刻只能夠接收到某一個指定item對象 if item.__class__.__name__ == 'FirstItem': job_title = item['job_title'] self.f1.write(job_title+'\n') else: job_desc = item['job_desc'] self.f2.write(job_desc) return item # settings 文件 要加UA 而且把ROBOTSTXT_OBEY 改成False
概念:使用多臺機器組成一個分佈式的機羣,在機羣中運行同一組程序,進行聯合數據的爬取。 原生的scrapy是不能夠實現分佈式: - 原生的scrapy中的調度器不能夠被共享 - 原生的scrapy的管道不能夠被共享 - 若是實現分佈式就必須使用scrapy-redis(模塊) - 能夠給原生的scrapy提供能夠被共享的管道和調度器 - pip install scrapy_redis 搭建流程: - 建立工程 - scrapy startproject 項目名稱 - 爬蟲文件 - scrapy genspider -t crawl 應用名 url地址 - 修改爬蟲文件: - 導報:from scrapy_redis.spiders import RedisCrawlSpider - 將當前爬蟲類的父類進行修改RedisCrawlSpider - allowed_domains,start_url刪除,添加一個新屬性redis_key(調度器隊列的名稱) - 數據解析,將解析的數據封裝到item中而後向管道提交 - 配置文件的編寫: - 指定管道: ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400 } - 指定調度器: # 增長了一個去重容器類的配置, 做用使用Redis的set集合來存儲請求的指紋數據, 從而實現請求去重的持久化 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用scrapy-redis組件本身的調度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 配置調度器是否要持久化, 也就是當爬蟲結束了, 要不要清空Redis中請求隊列和去重指紋的set。若是是True, 就表示要持久化存儲, 就不清空數據, 不然清空數據 SCHEDULER_PERSIST = True - 指定具體的redis: REDIS_HOST = 'redis服務的ip地址' REDIS_PORT = 6379 REDIS_ENCODING = ‘utf-8’ REDIS_PARAMS = {‘password’:’123456’} - 開啓redis服務(攜帶redis的配置文件:redis-server ./redis.windows.conf),和客戶端: - 對redis的配置文件進行適當的配置: - #bind 127.0.0.1 - protected-mode no - 開啓 - 啓動程序:scrapy runspider xxx.py - 向調度器隊列中扔入一個起始的url(redis的客戶端):lpush xxx www.xxx.com - xxx表示的就是redis_key的屬性值 - 問題: - 可能會出現,只要一臺電腦在工做,而其餘電腦沒有工做,把setting文件裏的 CONCURRENT_REQUESTS = 32 改的小一點
# 爬蟲文件 import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from scrapy_redis.spiders import RedisCrawlSpider from fbsPro.items import FbsproItem class TestSpider(RedisCrawlSpider): name = 'test' # allowed_domains = ['www.xxx.com'] # start_urls = ['http://www.xxx.com/'] #調度器隊列的名稱 redis_key = 'dongguan' rules = ( Rule(LinkExtractor(allow=r'type=4&page=\d+'), callback='parse_item', follow=True), ) def parse_item(self, response): a_list = response.xpath('//a[@class="news14"]') for a in a_list: item = FbsproItem() item['title']= a.xpath('./text()').extract_first() yield item
# item 文件 import scrapy class FbsproItem(scrapy.Item): # define the fields for your item here like: title = scrapy.Field()
#settings 文件 添加一下配置 ITEM_PIPELINES = { # 'fbsPro.pipelines.FbsproPipeline': 300, 'scrapy_redis.pipelines.RedisPipeline': 400 # 數字表示權重 } # 增長了一個去重容器類的配置, 做用使用Redis的set集合來存儲請求的指紋數據, 從而實現請求去重的持久化 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用scrapy-redis組件本身的調度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 配置調度器是否要持久化, 也就是當爬蟲結束了, 要不要清空Redis中請求隊列和去重指紋的set。若是是True, 就表示要持久化存儲, 就不清空數據, 不然清空數據 SCHEDULER_PERSIST = True REDIS_HOST = '***.***.**.***' # 鏈接redis 數據庫的地址 REDIS_PORT = 6379 #鏈接數據庫的端口
# 管道文件 因爲使用了 分佈式爬蟲,數據存儲在redis數據庫中,若是要作持久化存儲的話就必須將數據從redis數據庫中讀取出來再進行存儲
- 概念:用來《檢測》網站數據更新的狀況。只會爬取網站中更新出來的新數據。 - 核心:去重
# 爬蟲文件 import scrapy from qiubaiPro.items import QiubaiproItem import hashlib from redis import Redis class QiubaiSpider(scrapy.Spider): name = 'qiubai' conn = Redis(host='127.0.0.1',port=6379) # allowed_domains = ['www.xxx.com'] start_urls = ['https://www.qiushibaike.com/text/'] def parse(self, response): div_list = response.xpath('//div[@id="content-left"]/div') for div in div_list: #數據指紋:爬取到一條數據的惟一標識 author = div.xpath('./div/a[2]/h2/text() | ./div/span[2]/h2/text()').extract_first() content = div.xpath('./a/div/span//text()').extract() content = ''.join(content) item = QiubaiproItem() item['author'] = author item['content'] = content #數據指紋的建立 data = author+content hash_key = hashlib.sha256(data.encode()).hexdigest() ex = self.conn.sadd('hash_keys',hash_key) if ex == 1: print('有新數據更新......') yield item else: print('無數據更新!')
# items 文件 import scrapy class QiubaiproItem(scrapy.Item): # define the fields for your item here like: author = scrapy.Field() content = scrapy.Field()
# settings 文件 配置好 UA 而且把ROBOTSTXT_OBEY 改成False