Pyspider學習

簡介javascript

國人編寫的強大的網絡爬蟲系統並自帶強大的WebUI,採用Python語言編寫嗎,分佈式架構。支持多種數據庫後端 
pyspider中文網站:http://www.pyspider.cn/ 
源碼網址:https://github.com/binux/pyspider 
官方文檔: http://docs.pyspider.org/css

安裝:html

Phantomjs安裝 :前端

phantomjs下載地址:http://phantomjs.org/download.html
    下載編譯好的壓縮包解壓縮便可
  • 1
  • 2
  • 3

Pyspider安裝:java

pip install Pyspider 
安裝顯示界面: 
這裏寫圖片描述
啓動 Pysiderpython

pyspider all 
同時能夠經過制定 -c jsonfile 來指定啓動配置參數:mysql

{
  "taskdb": "mysql+taskdb://username:password@host:port/taskdb", "projectdb": "mysql+projectdb://username:password@host:port/projectdb", "resultdb": "mysql+resultdb://username:password@host:port/resultdb", "message_queue": "amqp://username:password@host:port/%2F", "webui": { "username": "some_name", "password": "some_passwd", "need-auth": true } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

可能會出現的錯誤:25555端口被佔用 
這裏寫圖片描述
緣由: 
lsof -i:port查詢佔用端口的pid 
原來phantomjs 在後臺已經啓動 
kill 相應進程再啓動 pyspider 錯誤消失git

[@bjzw_19_109 pyspider_pro]# lsof -i:25555 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME phantomjs 120202 root 17u IPv4 810232449 0t0 TCP *:25555 (LISTEN) [@bjzw_19_109 pyspider_pro]# kill -9 120202 [@bjzw_19_109 pyspider_pro]# lsof -i:25555 [@bjzw_19_109 pyspider_pro]# pyspider phantomjs fetcher running on port 25555
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在瀏覽器訪問 http://localhost:5000/github

#!/usr/bin/env python # -*- encoding: utf-8 -*- # Created on 2017-03-15 16:22:50 # Project: 120_ask from pyspider.libs.base_handler import * class Handler(BaseHandler): crawl_config = { } @every(minutes=24 * 60) def on_start(self): self.crawl('http://tag.120ask.com/jibing/pinyin/a', callback=self.index_page) @config(age=10 * 24 * 60 * 60) def index_page(self, response): for each in response.doc('div.s_m1.m980 a').items(): self.crawl(each.attr.href, callback=self.list_page) def list_page(self, response): for each in response.doc('div.s_m2.m980 a').items(): self.crawl(each.attr.href, callback=self.detail_page) def detail_page(self, response): return { "url": response.url, "disease_name" : response.doc("div.LogoNav.m980.clears p a:nth-child(2)").text(), "guake" : [x.text() for x in response.doc("div.p_rbox3 div.p_sibox3b span").items()] } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

這裏寫圖片描述

這裏寫圖片描述 
def on_start(self):是方法的入口代碼,當在控制檯點擊run按鈕時會調用這個方法 
@every(minutes=24*60) 這個設置是告訴scheduler(調度器)on_start方法天天執行一次。 
@config(age=10 * 24 * 60 * 60) 這個設置告訴scheduler(調度器)這個request(請求)過時時間是10天,10天內再遇到這個請求直接忽略。這個參數也能夠在self.crawl(url, age=10*24*60*60) 和 crawl_config中設置。web

上面的例子訪問的是靜態的地址:

#!/usr/bin/env python # -*- encoding: utf-8 -*- # Created on 2017-03-15 19:10:51 # Project: test_AJAX from pyspider.libs.base_handler import * class Handler(BaseHandler): crawl_config = { } @every(minutes=24 * 60) def on_start(self): self.crawl('https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start=0', callback=self.json_parser, validate_cert=False) @config(priority=2) def json_parser(self, response): return [{ "title":x['title'], "rate": x['rate'], "url" : x['url'] }for x in response.json['subjects']]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

這裏寫圖片描述

訪問ajax類型的:這是須要使用到phantomJS

PhantomJS是一個基於webkit的JavaScript API。它使用QtWebKit做爲它核心瀏覽器的功能,使用webkit來編譯解釋執行JavaScript代碼。任何你能夠在基於webkit瀏覽器作的事情,它都能作到。它不只是個隱形的瀏覽器,提供了諸如CSS選擇器、支持Web標準、DOM操做、JSON、HTML五、Canvas、SVG等,同時也提供了處理文件I/O的操做,從而使你能夠向操做系統讀寫文件等。PhantomJS的用處可謂很是普遍,諸如前端無界面自動化測試(須要結合Jasmin)、網絡監測、網頁截屏等。

#!/usr/bin/env python # -*- encoding: utf-8 -*- # Created on 2017-03-24 19:31:36 # Project: PhantomJS from pyspider.libs.base_handler import * class Handler(BaseHandler): crawl_config = { } @every(minutes=24 * 60) def on_start(self): self.crawl('http://movie.douban.com/explore',fetch_type='js', callback=self.phantomjs_parser, validate_cert=False) def phantomjs_parser(self, response): return [{ "title": "".join( s for s in x('p').contents() if isinstance(s, basestring) ).strip(), "rate": x('p strong').text(), "url": x.attr.href, } for x in response.doc('a.item').items()]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

架構: 
這裏寫圖片描述

組件和組件之間經過消息隊列連接,每個組件都包含了一個消息隊列,運行在組件各自的線程或者進程中 
組件:

Scheduler

任務優先級 
週期定時任務 
流量控制 
機遇時間週期的重抓取調度 
fetcher

method, header, cookie, proxy, etag, last_modified, timeout 等等抓取調度控制 
能夠經過適配相似 phantomjs 的webkit引擎支持渲染 
Phantomjs Fetcher: 
phantomjs 的工做過程相似於一個代理,經過javascript抓取和處理抓取過來的頁面,並返回正常的HTML

processor 
內置的pyquery,以jQuery解析頁面【也可用使用本身喜歡的解析工具】 
在腳本中徹底控制調度抓取的各項參數 
能夠向後鏈傳遞信息 
異常捕獲 
Pyspider的工做流

1:每個python腳本都會有一個on_start函數,當你在WebUI中點擊Run按鈕時,一個新的任務就會被遞交給調度器,等待執行 
2:Scheduler會分發這個task到相應的Fetcher中 
3:fetcher會抓取初始化的url並生成一個request和response對象,並傳遞給Processor 
4:Processor調用on_start函數而且傳遞一下新的url去爬取,但完成一個url的抓取工做時,processor會發送一個消息給Scheduler,告訴調度器這個任務完成。而且會發送一個新的task到scheduler中,同時會把爬取的結果發送到一個result_queue中 
5:Scheduler接收到新的task,會判斷這個任務是一個新的任務仍是須要從新抓取的任務,新的任務會把他們加入到任務隊列中,若是是從新須要抓取的任務,則須要看一下時間週期,知足要求的纔會放到任務隊列中去,而且按順序分發 
6:上面的過程會一直持續,直到程序死掉或者咱們手動中止。 
關於任務:

每個task是經過taskid來區別與其餘的任務的(默認taskid【md5(url)】),能夠經過重寫 
def get_taskid(self, task) 方法 
task有四種狀態: active、failed、success、bad-not used 
當且僅當task的狀態處於active時才能被scheduled調用 
失效任務重試: 
當任務失敗是,任務會默認重試3次 
第一次重試是在任務失敗的以後的30分鐘,1小時,6小時,更多的重試將會被推遲到12小時或者24小時以後

class MyHandler(BaseHandler):
    retry_delay = {
        0: 30, 1: 1*60*60, 2: 6*60*60, 3: 12*60*60, '': 24*60*60 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

注意:當咱們設置了age時,retry的時間不能長於age值

關於工程:

在webUI上會看到5種狀態 以下圖:

TODO:一個腳本剛剛被建立時的狀態 
STOP:能夠設置stop讓項目中止運行 
CHECKING:當一個項目在運行是被編輯是會自動的設置成此狀態並中止運行 
DEBUG/RUNNING :debug和running 
爬蟲的抓取速度:

rate:每秒執行多少個請求 
burst:設置併發數,如 rate/burst = 0.1/3 這個意思是爬蟲每10秒執行一個請求,可是前三個任務會同時執行,不會等10秒,第四個任務會等10秒再執行 
當咱們把group設置爲delete並把項目設置爲stop狀態 24小時 項目會自動刪除 
self.crawl 接口的幾個經常使用參數:

age:任務的重抓週期: 默認是-1(不在重抓) 
@config(age=10 * 24 * 60 * 60)#在10天以內遇到這個任務直接跳過 
def index_page(self, response): 
… 
priority:優先級 默認是0

def index_page(self): self.crawl('http://www.example.org/page2.html', callback=self.index_page) self.crawl('http://www.example.org/233.html', callback=self.detail_page, priority=1)
  • 1
  • 2
  • 3

auto_recrawl:重抓 默認是False

def on_start(self): self.crawl('http://www.example.org/', callback=self.callback, age=5*60*60, auto_recrawl=True)
  • 1
  • 2

params:參數

def on_start(self): self.crawl('http://httpbin.org/get', callback=self.callback,params={'a': 123, 'b': 'c'}) self.crawl('http://httpbin.org/get?a=123&b=c', callback=self.callback)
  • 1
  • 2
  • 3

data

def on_start(self): self.crawl('http://httpbin.org/post', callback=self.callback, method='POST', data={'a': 123, 'b': 'c'})
  • 1
  • 2

files:上傳文件

{field: {filename: 'content'}}
  • 1

proxy: 
目前僅支持http代理

class Handler(BaseHandler): crawl_config = { 'proxy': 'localhost:8080' }
  • 1
  • 2
  • 3
  • 4

fetch_type 
設置爲js開通支持JavaScriptfetcher。默認是None 
user_agent、headers、cookies、connect_timeout 
Response對象

response.url final url response.text response 內動,unicode編碼 response.content 字節表示 response.doc Pyquery對象 response.json類型 response.status_code response.orig_url:提交給self.crawl response.headers response.cookies response.time 抓取所需的時間 response.encoding :response.content的編碼方式
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Pyquery

http://www.w3school.com.cn/cssref/css_selectors.asp 
1.在pyquery中使用response.doc就能夠直接實例化一個pyquery對象,就能夠直接在裏面使用pyquery方法了, 
2.html()和text() ——獲取相應的HTML塊或文本塊,

例:html:"<head><title>hello</title></head>" response.doc('head').html()#返回<title>hello</title> response.doc('head').text()#返回hello
  • 1
  • 2
  • 3

3.根據HTML標籤來獲取元素,

例:html:'<div><p>test 1</p><p>test 2</p></div>' response.doc('p')#返回[<p>,<p>] print response.doc('p')#返回<p>test 1</p><p>test 2</p> print response.doc('p').html()#返回test 1
  • 1
  • 2
  • 3
  • 4

注意:當獲取到的元素不僅一個時,html()、text()方法只返回首個元素的相應內容塊 
4.eq(index) ——根據給定的索引號獲得指定元素

接上例,若想獲得第二個p標籤內的內容,則能夠:
print response.doc('p').eq(1).html() #返回test 2 5.filter() ——根據類名、id名獲得指定元素, 例:html:"<div><p id='1'>test 1</p><p class='2'>test 2</p></div>" response.doc('p').filter('#1') #返回[<p#1>] response.doc('p').filter('.2') #返回[<p.2>]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

6.find() ——查找嵌套元素,

例:html:"<div><p id='1'>test 1</p><p class='2'>test 2</p></div>" response.doc('div').find('p')#返回[<p#1>, <p.2>] response.doc('div').find('p').eq(0)#返回[<p#1>]
  • 1
  • 2
  • 3

7.直接根據類名、id名獲取元素,

例:html:"<div><p id='1'>test 1</p><p class='2'>test 2</p></div>" response.doc('#1').html()#返回test 1 response.doc('.2').html()#返回test 2
  • 1
  • 2
  • 3

8.獲取屬性值,

例:html:"<p id='my_id'><a href='http://hello.com'>hello</a></p>" response.doc('a').attr('href')#返回http://hello.com response.doc('p').attr('id')#返回my_id
  • 1
  • 2
  • 3

9.獲取內容的一部分能夠用分割字符串法:

例:html:"<p id='my_tel'>姓名 電話</p>" response.doc('#my_tel').text().split(' ')[0]用來取「姓名」 response.doc('#my_tel').text().split(' ')[0]用來取「電話」
  • 1
  • 2

最後再說一個模擬瀏覽器登錄的例子: 
爬取豆瓣讀書購物車,必須登陸

#!/usr/bin/env python # -*- encoding: utf-8 -*- # Created on 2017-03-28 16:27:57 # Project: crawl_DouBan from pyspider.libs.base_handler import * class Handler(BaseHandler): crawl_config = { } @every(minutes=24 * 60) def on_start(self): self.crawl('https://read.douban.com/account/wishlist', fetch_type='js',callback=self.detail_page,validate_cert=False,method="POST",headers={'User-Agent':'Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1','Host':'read.douban.com',"Accept":"application/json, text/javascript, */*; q=0.01","Connection": "keep-alive","Origin":"https://read.douban.com","Referer":"https://read.douban.com/account/wishlist","X-CSRF-Token":"uY6-","X-Requested-With":"XMLHttpRequest"},cookies={"ll":"108288", "bid":"4wabuJcYPyo","gr_user_id":"8df64966-e4c4-4150-9180-088f5041759b","ct":"y","ps":"y", "_ga":"GA1.2.2060938267.1489561978","gr_cs1_dec6e52e-b09e-4a55-81b6-b70e66983b20":"user_id%3A1","ap":"1","viewed":"26925834_1046265","gr_cs1_29a6eb10-c9ee-42dc-82f9-c6bb8970bcec":"user_id%3A0","ue":"510264954@qq.com","dbcl2":"159629853:vBrocExx16U","ck":"uY6-","_vwo_uuid_v2":"5B069C1533F3E2DAF37D8FFF1F80BB3A|e5878a8eaffcd79cc9e8ea57048e3bf1", "__utma":"30149280.2060938267.1489561978.1490612245.1490687748.8","__utmb":"30149280.31.10.1490687748", "__utmc":"30149280","__utmz":"30149280.1490687748.8.5.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided)","__utmv":"30149280.15962","push_noty_num":"0", "push_doumail_num":"0","_pk_ref.100001.a7dd":"%5B%22%22%2C%22%22%2C1490689343%2C%22https%3A%2F%2Fbook.douban.com%2Fsubject%2F26959159%2F%3Ficn%3Dindex-editionrecommend%22%5D", "gr_session_id_22c937bbd8ebd703f2d8e9445f7dfd03":"7eff0ba3-0e18-490a-97eb-f4ad7f8d01ec","gr_cs1_7eff0ba3-0e18-490a-97ebf4ad7f8d01ec":"user_id%3A1","_ga":"GA1.3.2060938267.1489561978", "_pk_id.100001.a7dd":"ddb145f17d799a99.1490601416.2.1490690002.1490601424.", "_pk_ses.100001.a7dd":"*; _gat=1"}) #"guake" : [x.text() for x in response.doc("div.p_rbox3 div.p_sibox3b span").items()] @config(priority=2) def detail_page(self, response): doc_body = response.doc('body') account = doc_body('li.more-active a.name.bn-more').text() book_info_dict = {} for item in doc_body('section.wishlist tbody tr').items(): book_url = item('td.td-name a').attr.href book_name = item('td.td-name a').text() book_price = item('td.td-price span').text() book_author = item('td span.author').text() book_info_dict[book_name] = ' '.join([book_url,book_name,book_price,book_author]) return { "url": response.url, "account":account, "book_list": book_info_dict }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

這裏寫圖片描述

相關文章
相關標籤/搜索