Python中網路頁面抓取和頁面分析

來源:互聯網
上載者:User

轉載本文請以連結形式註明出處。

1.前言

        Python的網路抓取有很多包可以實現,比如:urllib、urllib2、httplib、httplib2。其中httplib、httplib2是專門處理與http相關的;而urllib、urllib2是藉助於httplib、httplib2實現的,相當於在httplib、httplib2上又封裝了一層來進行處理web資料。而urllib2是urllib的高版本,httplib2是httplib的高版本。

        這裡我給出一個學習python庫的網址,裡面有各種lib庫的講解:http://docs.python.org/library/index.html  。有興趣的讀者可以看一下。

        由於最近在使用httplib2進行網上抓取資料,所以下面對httplib2進行介紹。

2.httplib2

(1)安裝      

         httplib2的安裝:首先下載python的httplib2的安裝包,為:http://code.google.com/p/httplib2/downloads/list;其次, 在dos視窗下進入httplib2的解壓目錄,執行命令:python setup.py install  。 即完成安裝。

(2)使用講解

        下面再給出一個httplib2的學習地址,是httplib2的一個wiki,裡面有幾個httplib2的簡單例子:http://code.google.com/p/httplib2/wiki/Examples 。

(a)下面給出第一個httplib2的例子,根據weibo使用者的ID,抓取使用者相關資訊:

         這個例子涉及到cookies的使用,下面簡單介紹一下cookies的問題:

         當使用者抓取的網頁需要登陸時,一般的登入頁面只需要使用者名稱和密碼,此時我們可以採用username和password的python代碼抓取網頁。但是當使用者頻繁的登入抓取時,此時登入頁面就需要輸入使用者名稱、密碼和一個隨機數字圖片驗證,圖片驗證無法屏蔽掉。此時,我們這裡可以採用先擷取使用者登陸後的cookies,再以http的get請求的方式向使用者發送headers(該headers包含使用者登陸後的cookies資訊),這樣就避免了登陸。

        擷取cookies的方法我感覺有2種,一種是手工方式:通過fiddler軟體來查看,當使用者在瀏覽器裡輸入請求的網址時,通過fiddler就可以查看使用者的http的響應,具體的過程如下面2張圖片所示,第一張圖片是fiddler查看cookies的header,第二張圖是拷貝該header:

       

        第二種擷取cookies的方式,可以第一次通過使用者名稱和密碼向url發出請求,然後將請求後的response中的headers的cookies儲存下來,下次就可以根據cookies去登入該url並擷取頁面內容。

        好,cookies介紹完畢,咱們回來,下面給出第一個demo(通過cookie登陸擷取微博頁面,並抓取微博使用者資訊):

#!/usr/bin/python# -*-coding:utf-8 -*-import httplib2import urllib2import re #Regex模組class WeiboClass: #定義一個weibo類    #擷取指定url的網頁內容    def get_content(self,url,headers,id):        http=httplib2.Http()        response,content=http.request(url+str(id),'GET',headers=headers)        #print url+str(id)        return content.decode('unicode-escape').encode('utf-8')    #判斷weibo的使用者是否是企業使用者    def is_company(self,url,headers,id):        content=self.get_content(url,headers,id)        title=r'行業'        company_title=re.compile(title)         if company_title.search(content): #使用Regex對title進行匹配                    return 1        else:            return 0            #擷取使用者的weibo資訊:ID,首頁url,暱稱    def get_info(self, url,headers,id):        flag=self.is_company(url,headers,id)        content=self.get_content(url,headers,id)        if flag==0:  #如果使用者是個人使用者                        #print content            #微博ID            id_flag=r'\$CONFIG\[\'oid\'\] = \'([0-9].+?)\';'            id_re=re.compile(id_flag)            id_regx=id_re.search(content)            id=id_regx.group(1)            print id            #微博url            url_flag=r'<meta http-equiv="mobile-agent" content="format=xhtml;" url="weibo.cn/(.+?)\?'            url_re=re.compile(url_flag)            url_regx=url_re.search(content)            url_0=url_regx.group(1)            url='http://weibo.com/'+url_0            print url                        #暱稱            name_flag='<div class="name clearfix">.+?<div class="left">(.+?)<'            name_re=re.compile(name_flag,re.S)            name_regx=name_re.search(content)            name=name_regx.group(1)            name=name.decode('utf-8').encode('GBK')            print namedef main():                headers={"cookie":'NSC_wjq_xfjcp.dpn_w3.6_w4=ffffffff0941137b45525d5f4f58455e445a4a423660; SUS=SID-1806453925-1342851885-XD-hgrjc-03210d75ca203f3ad0d57666a05ae49d; SUE=es%3Deabd0a14bc6e6123c5c4d058d9a2c96f%26ev%3Dv1%26es2%3Db50ae59b82b457a1ba54b2b7708fbb5b%26rs0%3DdP4RVYzORRwV64PFw6wRdNGBk0HP47V8C5SXUp%252F7Q9K2RcduYt4ECQbEDNZk%252Bs8GHDpW5wk%252B3%252FmYKP12zIyQbD1bUMd2wNBgdRX45p2rygizXgHMjH%252FFnU53HJFC2OfSvEHZADJkZD%252BTdLHidgoyy4maajVHi%252B%252B1en0zIKIf3mo%253D%26rv%3D0; SUP=cv%3D1%26bt%3D1342851885%26et%3D1342938285%26d%3Dc909%26i%3D4dc3%26us%3D1%26vf%3D0%26vt%3D0%26ac%3D0%26uid%3D1806453925%26user%3Dqipeng.sdsy%2540163.com%26ag%3D4%26name%3Dqipeng.sdsy%2540163.com%26nick%3D%25E4%25B8%2580%25E5%258C%25B9%25E5%259C%25A8%25E8%25B7%25AF%25E4%25B8%258A%25E7%259A%2584%25E9%25A9%25AC%26fmp%3D%26lcp%3D; SSOLoginState=1342851885; ads_ck=1; UOR=hao.360.cn,weibo.com,spr_web_360_hao360_weibo_t001:1342851234578; _olympicMedalsTable=; ULV=1342851891453:53:53:53:3919105774821.296.1342851890484:1342851070843; SinaRot/u/1806453925=25; un=qipeng.sdsy@163.com; __utma=182865017.1486634646.1342787238.1342787238.1342787238.1; __utmz=182865017.1342787238.1.1.utmcsr=weibo.com|utmccn=(referral)|utmcmd=referral|utmcct=/u/1806453925; myuid=1806453925; wvr=4; ALF=1343135290; SinaRot/u/1806453925%3Fgid%3D201108260278357557=77; SinaRot/u/1806453925%3Fc%3Dspr_web_360_hao360_weibo_t001=59; _s_tentry=-; Apache=3919105774821.296.1342851890484; SINAGLOBAL=3919105774821.296.1342851890484; BAYEUX_BROWSER=4e70u68yfp1x9xreh4wbcw2jfhs; JSESSI\ONID=3nvoas5ykh0e'}        url='http://weibo.com/'    print headers    page = WeiboClass()    page.get_info(url,headers,1842764911)if __name__ == "__main__":    main()

(b)下面給出httplib2的第二個例子

#!/usr/bin/python# -*-coding:utf-8 -*-import sys;import os;import urllib2;import httplib2;import Cookie;import random;import re;import time;from urllib import urlencode;import hashlib;import datetime;import socket;#httplib2.debuglevel=1;def error_log(log):    sys.stderr.write("%s\n" % (log));class ErrorCode:    Succ         = 0;    Unknown      = -1; # 未知原因class SimBrowser :    socket.setdefaulttimeout(10); # 逾時限制 10秒    UserAgent="";    cookie=None;    httplink = httplib2.Http();        httplink.follow_redirects = False;    hostname="";        def __init__(self, cookie, UserAgent="Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727)"):        self.cookie = cookie;        self.UserAgent = UserAgent;                    def gen_cookie_str(self):        cookiestr = '; '.join(self.cookie.output(attrs=[], header="").split('\r\n'));        if len(cookiestr) <= 0:            return "";        else:            return cookiestr;    def prepare_request_header(self, header):        newheader = {};        cookiestr = self.gen_cookie_str();        if len(cookiestr) > 0:            newheader['Cookie'] = cookiestr;                # set agent        newheader['User-Agent'] = self.UserAgent;                # replace or append user specified values in header        for key in header.keys():            newheader[key] = header[key];        return newheader;    # maintain cookies    def maintain_cookie(self, response_header):        if 'set-cookie' in response_header:            self.cookie.load(response_header['set-cookie']);                def get_redirect_url(self, prevurl, res):        if 'location' not in res:            error_log('no location in res');            return "";        location = res['location'];        if len(location) <= 0:            error_log('location length is zero');            return "";        # check location contain fullpath of target        if location.find("http://") != 0:            p = re.compile(r"[(http://)]*[.\-_0-9A-Za-z]+");            m = p.match(prevurl);            if m != None:                                host = m.group();                return host + location;            else:                error_log('cannot get host link');                host = "";        else:            return location;    def request(self, url, method="GET", headers={}, body="", follow_redirects=False):         newheaders = self.prepare_request_header(headers);        newurl = url;        newbody = body;        while (True):            try:              res, content = self.httplink.request(newurl, method=method, headers=newheaders, body=newbody);              self.maintain_cookie(res);            except Exception , what:                try:                  res, content = self.httplink.request(newurl, method=method, headers=newheaders, body=newbody);                  self.maintain_cookie(res);                except Exception , what:                     try:                      res, content = self.httplink.request(newurl, method=method, headers=newheaders, body=newbody);                      self.maintain_cookie(res);                    except Exception , what: # 訪問擷取 三次 不成功返回失敗                        res='';                        content='';                        break;            # check redirects            if follow_redirects==False:                break;            elif res.status in(300, 301, 302):                                prevurl = newurl;                newheaders = self.prepare_request_header({});                newurl = self.get_redirect_url(prevurl, res);                body = "";                method="GET";                if len(url) > 0:                    continue;                else:                    sys.stderr.write("Error:failed to get redirect location\n");                    break;            else:                break;        return res, content;def main():    cookie = Cookie.SimpleCookie();    sim = SimBrowser(cookie);        aurl='http://s.weibo.com/weibo/computer&Refer=STopic_box';    myCookie = ('UOR=,weibo.com,; myuid=1369574593; un=zhaolianxiang@126.com; \un=zhaolianxiang@126.com; wvr=4; __utma=15428400.565128140.1342666019.13426660\19.1342666019.1; __utmz=15428400.1342666019.1.1.utmcsr=blog.sina.com.cn|utmccn\=(referral)|utmcmd=referral|utmcct=/s/blog_84313fa001010n90.html; NSC_wjq_xfjc\p.dpn_w3.6_w4=ffffffff0941010945525d5f4f58455e445a4a423660; SSOLoginState=1342\867370; _s_tentry=login.sina.com.cn; Apache=9964875415898.86.1342867464581; UL\V=1342867464624:7:7:4:9964875415898.86.1342867464581:1342514299919; SUE=es%3D6\c406ebb66f15ce0e5b852efa908d728%26ev%3Dv1%26es2%3D658950facb6ad8c9c8627639f31a\61de%26rs0%3Df6AWRRwcy3r7HJ7y1mdnQR5icnbFHj6Qt%252F6Og2%252FaDBwMtLGJbQhanphxu\EWsXCI1CPdl1yhB%252BHNobWvCmQPmF0xjrJhrvxCWAoiiE7D9cPDXQVOvlQPDsAopv10Un5DDuES\%252FZcPXtwnaCYnD5hcMAoDinTKgBxbeZ%252FBSiLzvEdQ%253D%26rv%3D0; SUP=cv%3D1%26b\t%3D1343009147%26et%3D1343095547%26d%3Dc909%26i%3D3d45%26us%3D1%26vf%3D0%26vt%\3D0%26ac%3D1%26uid%3D1842764911%26user%3Dzhaolianxiang%2540126.com%26ag%3D4%26\name%3Dzhaolianxiang%2540126.com%26nick%3DSean%26fmp%3D%26lcp%3D2011-12-25%252\012%253A59%253A36; SUS=SID-1842764911-1343009147-XD-cyk5d-215c6a72f1b3a340c301\533e2b4ce49d; ALF=1343095212; ads_ck=1; SinaRot/z/zhaolianxiang=44; SINAGLOBAL\=9964875415898.86.1342867464581; _olympicMedalsTable=; USRHAWB=usrmdins213_206');    headers={'Cookie':myCookie,'Content-Type':'application/x-www-form-urlencoded'};#    print "myCookie:",myCookie;#    body={'path':'GET/material','userid':self.BaiduId,'token':self.token,'params':'{"level":"useracct","fields":["wregion","wbudget","userstat"]}'};    #以http的方式向aurl發出get請求,同時將http的頭headers發送過去    #這個headers包含了使用者的登陸的cookies資訊。cookies的擷取可以通過fiddler軟體來查看。    res, content = sim.request(aurl,'GET', headers=headers);    print "res:",res  #輸出http的響應response    #將擷取的網頁內容先解碼,再以utf-8的形式編碼    print "content:",content.decode('unicode-escape').encode('utf-8');    if __name__ == "__main__":    main();                

註:上面的兩個程式中的cookies均已失效,需要您通過我上面介紹的方法替換cookies中的內容。

(3)總結

使用cookie登陸抓取指定頁面,可以像下面這樣,這是一個最簡潔的代碼

#!/usr/bin/python# coding:utf-8 import httplib2import urllib2import re #Regex模組class PageClass:    #擷取指定url的網頁內容    def get_page(self,url,headers):        http=httplib2.Http()        response,content=http.request(url,'GET',headers=headers)        #return content.decode('unicode-escape').encode('utf-8')        return content.decode('unicode-escape').encode('utf-8')        def main():                headers={"cookie":'your cookie'}    url = 'http://fengchao.baidu.com'    #print headers    page = PageClass()    content = page.get_page(url,headers)    print contentif __name__ == "__main__":    main()

只需要輸入url和headers就可以抓取到指定的頁面。這裡需要輸入你自己的cookie。上面的代碼執行後的結果如下,可以看到我們抓取到了內容:

3.XPath

        將抓取到的頁面源碼,如何進行解析,一般採用下面三種技術:
        (1)lxml的xpath:基於XML的語義進行解析的(推薦)。
        (2)Regex(RE):基於純文字的處理。
        (3)純字串處理(不推薦)

        總結:RE對付簡單的頁面沒有問題,如果頁面結構複雜度較高的時候,建議採用xpath,因為此時設計一個合適的RE pattern可能會遠比寫一個xpath要複雜。

        lxml的:http://pypi.python.org/pypi/lxml/2.3

       上面介紹的httplib2的2個樣本均是通過RE(Regex)來進行頁面解析的。下面我們介紹XPath來進行頁面解析想要的內容。

        XPATH基本上是用一種類似分類樹的方法來描述在XML文檔中的路徑。比如用“/”來作為上下層級間的分隔。第一個“/”表示文檔的根節點(注意,不是指文檔最外層的tag節點,而是指文檔本身)。比如對於一個HTML檔案來說,最外層的節點應該是”/html”。同樣的,“..”和“.”分別被用來表示父節點和本節點。
        XPATH返回的不一定就是唯一的節點,而是合格所有節點。比如在HTML文檔裡使用“/html/head/scrpt”就會把head裡的所有script節點都取出來。
        為了縮小定位範圍,往往還需要增加過濾條件。過濾的方法就是用“[”“]”把過濾條件加上。比如在HTML文檔裡使用“/html/body/div[@id='main']”,即可取出body裡id為main的div節點。其中@id表示屬性id,類似的還可以使用如@name, @value, @href, @src, @class….  。

         而函數text()的意思則是取得節點包含的文本。比如:<div>hello<p>world</p>< /div>中,用”div[text()='hello']“即可取得這個div,而world則是p的text()。

          函數position()的意思是取得節點的位置。比如“li[position()=2]”表示取得第二個li節點,它也可以被省略為“li[2]”。

          不過要注意的是數字定位和過濾條件的順序。比如“ul/li[5][@name='hello']”表示取ul下第五項li,並且其name必須是hello,否則返回空。而如果用“ul/li[@name='hello'][5]”的意思就不同,它表示尋找ul下第五個name為”hello“的li節點。
          此外,“*”可以代替所有的節點名,比如用”/html/body/*/span”可以取出body下第二級的所有span,而不管它上一級是div還是p或是其它什麼東東。
而 “descendant::”首碼可以指代任意多層的中間節點,它也可以被省略成一個“/”。比如在整個HTML文檔中尋找id為“leftmenu”的 div,可以用“/descendant::div[@id='leftmenu']”,也可以簡單地使用“ //div[@id='leftmenu']”。
        至於“following-sibling::”首碼就如其名所說,表示同一層的下一個節點。”following-sibling::*”就是任意下一個節點,而“following-sibling::ul”就是下一個ul節點。

        下面給出一個樣本,其功能為提取百度首頁搜尋方塊上面的導航條的內容:

#該程式是對百度的首頁進行分析,並提取出其搜尋方塊上面的導航條import httplib2import urllib2import refrom lxml import etreedef main():    http = httplib2.Http()    response,content = http.request("http://www.baidu.com",'GET')    print "response:",response    print "content:",content    tree = etree.HTML(content)    #上面的注釋為要尋找的部分html    #<p id=nv><a href=http://news.baidu.com>新聞</a><b>網頁</b>    #<a href=http://tieba.baidu.com>貼吧</a><a href=http://zhidao.baidu.com>知道</a>    #<a href=http://mp3.baidu.com>MP3</a><a href=http://image.baidu.com>圖片</a>    #<a href=http://video.baidu.com>視頻</a><a href=http://map.baidu.com>地圖</a></p>    #下面開始尋找id為nv的p標籤下的所有<a>的href值    hyperlinks = tree.xpath(u'//p[@id="nv"]/a/@href')    print "hyperlinks:",hyperlinks    for hyperlink in hyperlinks:        print "hyperlink:",hyperlink            #尋找id為nv的p標籤下的所有<a>節點    a_nodes = tree.xpath(u'//p[@id="nv"]/a')    print "a_nodes_length:",len(a_nodes)    for a_node in a_nodes:        print "<a>:",a_node.text,a_node.attrib['href']    print "\n"    #通過Regex尋找<p id="nv">的標籤內容,匹配的內容為Regex中的"()"內的內容       name_flag='<p id="nv">(.+?)</p>'    name_re=re.compile(name_flag,re.S)    name_regx=name_re.search(content)    print name_regx    name=name_regx.group(1)    print "name:",name        if __name__ == "__main__":    main()

其執行結果為:

下面對其進行分析:

        首先,對www.baidu.com進行http的get請求,然後將相應的頁面結果進行分析。這裡我使用了3種方式進行頁面分析,前2種方式為使用xpath提取,第3種方式為通過Regex匹配提取。程式中有詳細的注釋。


4.HTMLParser

        該模組是用來解析HTML元素的。可以從HTML中篩選出指定的標籤。下面給出一個例子,讀取百度首頁www.baidu.com中的所有連結,並列印出來。

import HTMLParserimport urllibimport sys#定義HTML解析器class parseLinks(HTMLParser.HTMLParser):    #該方法用來處理開始標籤的,eg:<div id="main">    def handle_starttag(self, tag, attrs):        if tag == 'a':  #如果為<a>標籤            #name為標籤的屬性名稱,如href、name、id、onClick等等            for name,value in attrs:                    if name == 'href': #這時選擇href屬性                    print "name_value: ",value  #href屬性的值                    print "first tag:",self.get_starttag_text() #<a>標籤的開始tag                    print "\n"if __name__ == "__main__":    #建立HTML解析器的執行個體    lParser = parseLinks()    #開啟HTML檔案    lParser.feed(urllib.urlopen("http://www.baidu.com").read())    lParser.close()

        但調用feed函數時,會自動調用handle_starttag函數,這裡的handle_starttag函數是對原函數的重寫。handle_starttag(self,tag,attrs)中的參數tag是標籤的名字;參數attrs是一個(name,value)索引值對,是通過尋找到tag的<>括弧來確定的,其中name是tag的<>中的屬性名稱,value是去除引號後的值。

       上面程式的執行結果如下:(下面的結果只是部分的螢幕)

5.Regex(RE)

      由於上面涉及到很到Regex的匹配問題。下面對Python中的RE使用進行簡單的示範和說明。下面給出一段,我測試用的code:

import re    text = "JGood is a handsome booy, he is cool, clever, and so on..."  regex1 = re.match(r"\w*oo\w*", text)  if regex1:      print "regex1:" , regex1    print "result1:" ,regex1.group(0)  else:      print 'not match'print "\n"regex2 = re.compile(r'(\w*oo\w*)')print "result2:" , regex2.findall(text)print "\n"regex3 = re.compile(r'(\w*oo\w*).+?(\w*eve\w*).*')regex3_result = regex3.search(text)if regex3_result:     print "regex3:", regex3    print " result3:",regex3_result.group(1)," ",regex3_result.group(2)else:    print 'not match'

程式的運行結果:

下面簡單的講解一下:

        regex1是通過match方法進行匹配,match只匹配字串的開始,如果字串開始不符合Regex,則匹配失敗,函數返回None;regex2是通過findall,尋找所有滿足的匹配;regex3是通過search匹配整個字串,直到找到一個匹配。

        對於regex3_result.group(1),regex3_result.group(2)是什麼意思呢,我搞了半天才弄明白。group(i)表示匹配的Regex"()"中的內容。如regex3_result.group(1)表示匹配的Regex中第一個()內的內容,regex3_result.group(2)表示匹配的Regex中第二個“()”的對應的字串。group(0)表示整個運算式。group是從左向右計數的,從1開始。組可以被嵌套。計數的數值可以通過從左至右計算開啟的括弧數來確定。

再給出一個小例子,一目瞭然:

#!python>>> p = re.compile('(a(b)c)d')>>> m = p.match('abcd')>>> m.group(0)'abcd'>>> m.group(1)'abc'>>> m.group(2)'b'

6.如何判斷網路連接

有的軟體如果需要訪問網路,這時我們就需要定時地檢測網路的串連,下面的代碼給出了如何檢測網路連接:當串連時返回1;當網路斷掉時返回0。原理就是通過http去請求網頁,如果能夠請求到則網路正常,如果請求出現異常則網路斷掉。

def check_network():    import httplib2    try:        http = httplib2.Http()        resp, content = http.request("http://www.baidu.com")    except:        return 0    return 1

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.