python爬蟲小練習

網(wǎng)頁抓取

根據(jù)鏈接

從入口頁面開始抓取出所有鏈接福也,支持proxy、支持定義深度抓取攀圈、鏈接去重等暴凑,尚未做并發(fā)處理

code如下

import urlparse
import urllib2
import re
import Queue

#頁面下載
def page_download(url,num_retry=2,user_agent='zhxfei',proxy=None):
    #print 'downloading ' , url
    headers = {'User-agent':user_agent}
    request = urllib2.Request(url,headers = headers)
    opener = urllib2.build_opener()
    if proxy:
        proxy_params = {urlparse(url).scheme:proxy}
        opener.add_handler(urllib2.ProxyHandler(proxy_params))

    try:
        html = urllib2.urlopen(request).read()   #try : download the page
    except urllib2.URLError as e:                       #except : 
        print 'Download error!' , e.reason                  #URLError 
        html = None
        if num_retry > 0:                                   # retry download when time>0
            if hasattr(e, 'code') and 500 <=e.code <=600:
                return  page_download(url,num_retry-1)       
            
    if html is None:
        print '%s Download failed' % url
    else:
        print '%s has Download' % url
    
    return html

#使用正則表達式匹配出頁面中的鏈接
def get_links_by_html(html):
    webpage_regex = re.compile('<a[^>]+href=["\'](.*?)["\']', re.IGNORECASE)   
    return webpage_regex.findall(html)

#判斷抓取的鏈接和入口頁面是否為同站
def same_site(url1,url2):
    return urlparse.urlparse(url1).netloc == urlparse.urlparse(url2).netloc

def link_crawler(seed_url,link_regex,max_depth=-1):
    crawl_link_queue = Queue.deque([seed_url])
    seen = {seed_url:0}         # seen means page had download
    depth = 0
    
    while crawl_link_queue:
        url = crawl_link_queue.pop()
        depth = seen.get(url)
        if seen.get(url) > max_depth:
            continue
        links = []
        html = page_download(url)
        
        links.extend(urlparse.urljoin(seed_url, x) for x in get_links_by_html(html) if re.match(link_regex, x))

        for link in links:
            if link not in seen:
                seen[link]= depth + 1
                if same_site(link, seed_url):
                    crawl_link_queue.append(link)

        #print seen.values()
    print '----All Done----' , len(seen)
    return seen


if __name__ == '__main__':
    all_links = link_crawler('http://www.zhxfei.com',r'/.*',max_depth=1) 

運行結(jié)果:

http://www.zhxfei.com/archives has Download
http://www.zhxfei.com/2016/08/04/lvs/ has Download
...
...
http://www.zhxfei.com/2016/07/22/app-store-審核-IPv6-Olny/#more has Download
http://www.zhxfei.com/archives has Download
http://www.zhxfei.com/2016/07/22/HDFS/#comments has Download
----All Done----
根據(jù)sitmap

sitemap是相當于網(wǎng)站的地圖,于其相關的還有robots.txt赘来,一般都是在網(wǎng)站的根目錄下專門提供給各種spider现喳,使其更加友好的被搜索引擎收錄,定義了一些正規(guī)爬蟲的抓取規(guī)則

所有也可以這樣玩犬辰,將xml文件中的url拿出來拿穴,根據(jù)url去直接抓取網(wǎng)站,這是最方便的做法(雖然別人不一定希望我們這么做)

#!/usr/bin/env python
# _*_encoding:utf-8 _*_

# description: this modlue is load crawler By SITEMAP

import re
from download import page_download

def load_crawler(url):
    #download the sitemap
    sitemap = page_download(url)
    
    links = re.findall('<loc>(.*?)</loc>',sitemap)
    
    for link in links:
    
        page_download(link)
        
        if link == links[-1]:
        
            print 'All links has Done'
#     print links
    
load_crawler('http://example.webscraping.com/sitemap.xml')
小結(jié)

好了忧风,現(xiàn)在爬蟲已經(jīng)具備了抓取網(wǎng)頁的能力默色,然而他并沒有做什么事情,只是將網(wǎng)頁download下來狮腿,所以我們還要進行數(shù)據(jù)處理腿宰。也就是需要在網(wǎng)頁中抓取出我們想要的信息。

數(shù)據(jù)提取

使用Lxml提取

抓取網(wǎng)頁中的信息常用的的三種方法:

  • 使用正則表達式解析缘厢,re模塊吃度,這是最快的解決方案,并且默認的情況下它會緩存搜索的結(jié)果(可以借助re.purge()來講緩存清除)贴硫,當然也是最復雜的方案(不針對你是一只老鳥)
  • 使用Beautifulsoup進行解析椿每,這是最人性化的選擇,因為它處理起來很簡單英遭,然而處理大量數(shù)據(jù)的時候很慢间护,所以當抓取很多頁面的時候,一般不推薦使用
  • 使用Lxml挖诸,這是相對比較中性的做法汁尺,使用起來也比較簡單,這里我們選擇它對抓取的頁面進行處理

Lxml的使用有兩種方式:Xpath和cssselect多律,都是使用起來比較簡單的痴突,Xpath可以和bs一樣,使用find和find_all匹配parten(匹配模式)狼荞,用鏈型的結(jié)構描述DOM和數(shù)據(jù)的位置辽装。而cssselct直接是用了jQuery的選擇器來進行匹配,這樣對有前端功底的同學更加友好相味。

先給個demo試下:即將抓取的網(wǎng)頁http://example.webscraping.com/places/view/United-Kingdom-239 has Download

網(wǎng)頁中有個表格<table>,我們想要的信息都是存在body的表格中拾积,可以使用瀏覽器的開發(fā)者工具來省查元素,也可以使用firebug(Firefox上面的一款插件)來查看DOM結(jié)構

import lxml.html
import cssselect
from download import page_download

example_url = 'http://example.webscraping.com/places/view/United-Kingdom-239'

def demo():
    html = page_download(example_url, num_retry=2)

    result = lxml.html.fromstring(html)
    print type(result)
    td = result.cssselect('tr#places_area__row > td.w2p_fw')
    print type(td)
    print len(td)
    css_element = td[0]
    print type(css_element)
    print css_element.text_content()

執(zhí)行結(jié)果:

http://example.webscraping.com/places/view/United-Kingdom-239 has Download
<class 'lxml.html.HtmlElement'>
<type 'list'>
1
<class 'lxml.html.HtmlElement'>
244,820 square kilometres

可以看到,使用cssselect進行選擇器是拿到了一個長度是1的列表殷勘,當然列表的長度顯然和我定義的選擇器的模式有關此再,這個列表中每一項都是一個HtmlElement,他有一個text_content方法可以返回這個節(jié)點的內(nèi)容玲销,這樣我們就拿到了我們想要的數(shù)據(jù)输拇。

回調(diào)處理

接下來我們就可以為上面的爬蟲增加定義一個回調(diào)函數(shù),在我們每下載一個頁面的時候贤斜,做一些小的操作策吠。
顯然應該修改link_crawler函數(shù),并在其參數(shù)傳遞回調(diào)函數(shù)的引用瘩绒,這樣就可以針對不同頁面來進行不同的回調(diào)處理如:

def link_crawler(seed_url,link_regex,max_depth=-1,scrape_callback=None):
...

    html = page_download(url)   #這行和上面一樣
    if scrape_callback:
        scrape_callback(url,html)    
    links.extend(urlparse.urljoin(seed_url, x) for x in get_links_by_html(html) if re.match(link_regex, x)) #這行和上面一樣
...

接下來編寫回調(diào)函數(shù)猴抹,由于python的面向?qū)ο蠛軓姶螅赃@里使用回調(diào)類來完成锁荔,由于我們需要調(diào)用回調(diào)類的實例蟀给,所以需要重寫它的__call__方法,并實現(xiàn)在調(diào)用回調(diào)類的實例的時候阳堕,將拿到的數(shù)據(jù)以csv格式保存跋理,這個格式可以用wps打開表格。當然你也可以將其寫入到數(shù)據(jù)庫中恬总,這個之后再提

import csv
class ScrapeCallback():
    
    def __init__(self):
        self.writer = csv.writer(open('contries.csv','w+'))
        self.rows_name = ('area','population','iso','country','capital','tld','currency_code','currency_name','phone','postal_code_format','postal_code_regex','languages','neighbours')
        self.writer.writerow(self.rows_name)
        
    def __call__(self,url,html):
        if re.search('/view/', url):
            tree = lxml.html.fromstring(html)            
            rows = []
            for row in self.rows_name:
                rows.append(tree.cssselect('#places_{}__row > td.w2p_fw'.format(row))[0].text_content())
    
            self.writer.writerow(rows)

可以看到回調(diào)類有三個屬性:

self.rows_name這個屬性保存了我們的想要抓取數(shù)據(jù)的信息
self.writer這個類似文件句柄一樣的存在
self.writer.writerow這個屬性方法是將數(shù)據(jù)寫入csv格式表格

好了前普,這樣就可以將我們的數(shù)據(jù)持久化保存起來

修改下link_crawler的define:def link_crawler(seed_url,link_regex,max_depth=-1,scrape_callback=ScrapeCallback()):

運行看下結(jié)果:

zhxfei@zhxfei-HP-ENVY-15-Notebook-PC:~/桌面/py_tran$ python crawler.py 
http://example.webscraping.com has Download
http://example.webscraping.com/index/1 has Download      # /index 在__call__中的/view 所以不會進行數(shù)據(jù)提取
http://example.webscraping.com/index/2 has Download
http://example.webscraping.com/index/0 has Download
http://example.webscraping.com/view/Barbados-20 has Download
http://example.webscraping.com/view/Bangladesh-19 has Download
http://example.webscraping.com/view/Bahrain-18 has Download
...
...
http://example.webscraping.com/view/Albania-3 has Download
http://example.webscraping.com/view/Aland-Islands-2 has Download
http://example.webscraping.com/view/Afghanistan-1 has Download
----All Done---- 35

zhxfei@zhxfei-HP-ENVY-15-Notebook-PC:~/桌面/py_tran$ ls
contries.csv  crawler.py

打開這個csv,就可以看到數(shù)據(jù)都保存了:


完整代碼在這里:

#!/usr/bin/env python
# _*_encoding:utf-8 _*_

import urlparse
import urllib2
import re
import time
import Queue
import lxml.html
import csv

class ScrapeCallback():
    
    def __init__(self):
        self.writer = csv.writer(open('contries.csv','w+'))
        self.rows_name = ('area','population','iso','country','capital','tld','currency_code','currency_name','phone','postal_code_format','postal_code_regex','languages','neighbours')
        self.writer.writerow(self.rows_name)
        
    def __call__(self,url,html):
        if re.search('/view/', url):
            tree = lxml.html.fromstring(html)            
            rows = []
            for row in self.rows_name:
                rows.append(tree.cssselect('#places_{}__row > td.w2p_fw'.format(row))[0].text_content())
    
            self.writer.writerow(rows)

def page_download(url,num_retry=2,user_agent='zhxfei',proxy=None):
    #print 'downloading ' , url
    headers = {'User-agent':user_agent}
    request = urllib2.Request(url,headers = headers)
    opener = urllib2.build_opener()
    if proxy:
        proxy_params = {urlparse(url).scheme:proxy}
        opener.add_handler(urllib2.ProxyHandler(proxy_params))

    try:
        html = urllib2.urlopen(request).read()   #try : download the page
    except urllib2.URLError as e:                       #except : 
        print 'Download error!' , e.reason                  #URLError 
        html = None
        if num_retry > 0:                                   # retry download when time>0
            if hasattr(e, 'code') and 500 <=e.code <=600:
                return  page_download(url,num_retry-1)       
            
    if html is None:
        print '%s Download failed' % url
    else:
        print '%s has Download' % url
    
    return html

def same_site(url1,url2):
    return urlparse.urlparse(url1).netloc == urlparse.urlparse(url2).netloc

def get_links_by_html(html):
    webpage_regex = re.compile('<a[^>]+href=["\'](.*?)["\']', re.IGNORECASE)   #理解正則表達式
    return webpage_regex.findall(html)

def link_crawler(seed_url,link_regex,max_depth=-1,scarape_callback=ScrapeCallback()):
    crawl_link_queue = Queue.deque([seed_url])
    # seen contain page had find and it's depth,example first time:{'seed_page_url_find','depth'}
    seen = {seed_url:0}         
    depth = 0
    
    while crawl_link_queue:
        url = crawl_link_queue.pop()
        depth = seen.get(url)
        if seen.get(url) > max_depth:
            continue
        links = []
        html = page_download(url)
        
        links.extend(urlparse.urljoin(seed_url, x) for x in get_links_by_html(html) if re.match(link_regex, x))

        for link in links:
            if link not in seen:
                seen[link]= depth + 1
                if same_site(link, seed_url):
                    crawl_link_queue.append(link)

        #print seen.values()
    print '----All Done----' , len(seen)

    return seen


if __name__ == '__main__':
    all_links = link_crawler('http://example.webscraping.com', '/(index|view)',max_depth=2)

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市壹堰,隨后出現(xiàn)的幾起案子拭卿,更是在濱河造成了極大的恐慌,老刑警劉巖贱纠,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件峻厚,死亡現(xiàn)場離奇詭異,居然都是意外死亡并巍,警方通過查閱死者的電腦和手機目木,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來懊渡,“玉大人,你說我怎么就攤上這事军拟√曛矗” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵懈息,是天一觀的道長肾档。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么怒见? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任俗慈,我火速辦了婚禮,結(jié)果婚禮上遣耍,老公的妹妹穿的比我還像新娘闺阱。我一直安慰自己,他們只是感情好舵变,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布酣溃。 她就那樣靜靜地躺著,像睡著了一般纪隙。 火紅的嫁衣襯著肌膚如雪赊豌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天绵咱,我揣著相機與錄音碘饼,去河邊找鬼。 笑死悲伶,一個胖子當著我的面吹牛派昧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拢切,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼蒂萎,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了淮椰?” 一聲冷哼從身側(cè)響起五慈,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎主穗,沒想到半個月后泻拦,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡忽媒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年争拐,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晦雨。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡架曹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出闹瞧,到底是詐尸還是另有隱情绑雄,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布奥邮,位于F島的核電站万牺,受9級特大地震影響罗珍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜脚粟,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一覆旱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧核无,春花似錦扣唱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至已慢,卻和暖如春曲聂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背佑惠。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工朋腋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人膜楷。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓旭咽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赌厅。 傳聞我的和親對象是個殘疾皇子穷绵,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 在上一篇中 , 我們構建了一個爬蟲特愿, 可以通過跟蹤鏈接的方式下載我們所需的網(wǎng)頁仲墨。 但是爬蟲在下載網(wǎng)頁之后又將 結(jié)果...
    楓灬葉閱讀 1,961評論 0 5
  • Python版本管理:pyenv和pyenv-virtualenvScrapy爬蟲入門教程一 安裝和基本使用Scr...
    inke閱讀 35,294評論 7 93
  • scrapy學習筆記(有示例版) 我的博客 scrapy學習筆記1.使用scrapy1.1創(chuàng)建工程1.2創(chuàng)建爬蟲模...
    陳思煜閱讀 12,696評論 4 46
  • 感恩老公開了四個多小時的車,顧不上休息在家?guī)Ш⒆幼屛乙粋€人自由上街逛揍障。 感恩老公沒有因為我忘記帶鑰匙目养,耽誤吃飯時間...
    莀寶貝閱讀 231評論 0 1