国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Python網頁爬蟲(一)

2019-11-14 17:16:21
字體:
來源:轉載
供稿:網友

      很多時候我們想要獲得網站的數據,但是網站并沒有提供相應的API調用,這時候應該怎么辦呢?還有的時候我們需要模擬人的一些行為,例如點擊網頁上的按鈕等,又有什么好的解決方法嗎?這些正是python和網頁爬蟲的應用場景。python是一種動態解釋性語言,簡單的語法和強大的庫支持使得python在數據收集、數據分析、網頁分析、科學計算等多個領域被廣泛使用。

      本文主要總結一下如何用python自己寫一個簡單的爬蟲,以及可能出現的問題與解決方法。

      首先介紹一下大概的思路,首先需要在程序中連接網站并發送GET請求得到html文件,然后需要解析html文件,根據某種規律將需要的信息提取出來,然后用合適的方式處理數據并保存。

(一)python中用于http連接的庫——urllib2

  首先放上python文檔的鏈接,英文好的同學可自由閱讀:https://docs.python.org/2/library/urllib2.html?highlight=urllib2#module-urllib2

  這個庫是python用來解析URL主要是HTTP的主要工具,而且提供了對身份認證、重定向、cookie等的支持,具體用法可以仔細的讀文檔,這里作為最簡單的場景,我介紹如何打開一個URL并得到對應的HTML頁面。只要使用一個函數:

  urllib2.urlopen(url[, data[, timeout[, cafile[, capath[, cadefault[, context]]]]])

  1、函數的參數url可以是一個string也可以是一個request對象,如果提供了參數列表中data的值(dict類型)那么函數將發送POST請求,默認情況是發送GET。另外,很多人說沒法設置連接的timeout,然后提供了一堆類似修改socket全局timeout的方法,其實這里可以直接設定的嘛!

  2、函數的返回值。函數返回一個類文件的東西,which means,函數返回的值可以當一個文件類來操作,只是多了三個方法:

    • geturl()返回這個html對應的url
    • info()   返回這個html文件的元信息例如headers
    • getcode()返回網站的返回的狀態碼,如200表示OK,404表示not found之類。

  3、函數拋出的異常:URLError例如發生無網絡連接這種事情。socket.timeout如果你設置了timeout參數,超時后便會拋出此異常。

  所以這里的代碼可以這樣寫:

 1 import urllib2 2 import socket 3 def gethtml(url): 4     try: 5         f = urllib2.urlopen(url,timeout=10) 6         data = f.read() 7     except socket.timeout, e: 8         data = None 9         PRint "time out!"10         with open("timeout",'a') as log:11             log.write(url+'/n')12     except urllib2.URLError,ee:13         data = None14         print "url error"15     finally:16         return data

  這樣就可以得到對應URL的網頁的html代碼了(但是在具體應用你可能會碰到蛋疼的問題,我碰到的會列在下面)

  (二)對html的解析

  獲得了網頁源代碼后我們需要從里面提取出接下來要爬取的URL或者我們需要的數據,這就需要對html解析。2.7版本的python中,內置的解析類是HTMLParser,庫文檔:https://docs.python.org/2/library/htmlparser.html?highlight=htmlparser

  其實,你要做的全部,就是繼承這個類,然后把里面的接口函數實現,他看起來是這樣子的:

 1 from HTMLParser import HTMLParser 2  3 # create a subclass and override the handler methods 4 class MyHTMLParser(HTMLParser): 5     def handle_starttag(self, tag, attrs): 6         print "Encountered a start tag:", tag 7     def handle_endtag(self, tag): 8         print "Encountered an end tag :", tag 9     def handle_data(self, data):10         print "Encountered some data  :", data

  例如對于一個最簡單的html文件:

1 <html>2     <head>3         <title>Test</title>4     </head>5     <body>6         <h1>Parse me!</h1>7     </body>8 </html>    

  當程序遇到每一個標簽的開始時便會調用handle_starttag函數,遇到標簽結束就會調用handle_endtag,遇到標簽之間的數據就會調用handle_data可以結合最后貼出的示例代碼進一步理解。想要解析這個文件只要如下寫:

#例如借用上面的gethtml函數def parse():    html = gethtml("http://baidu.com",timeout=10)    parser = MyParser()    parser.feed(html)    parser.close()

  調用feed函數把html放入緩沖區,調用close函數強制parser開始解析html

  (三)問題與解決方法:

  1、得到的html文檔是亂碼

  這個問題可能有很多原因引起,最主要的兩個是:網頁的編碼方式和你的解碼方式不匹配。關于編碼和解碼推薦讀一下這個博客:http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386819196283586a37629844456ca7e5a7faa9b94ee8000

  這時候你會明白編碼問題確實導致了文件的不可讀。所以你要正確獲得網頁的編碼方式,然后調用對應的解碼器,代碼可能是這樣的:

f = urllib2.urlopen(url,timeout=10)data = f.read()    # decode the htmlcontentType = f.headers.get('Content-Type')if contentType.find("gbk"):    data = unicode(data, "GBK").encode("utf-8")elif contentType.find("utf-8"):    pass

  當然,編碼方式可能多種,不止GBK

  2、還是亂碼

  如果還是亂碼,可能你依然沒有選對解碼器,也可能是網頁被壓縮過了,這時候你要先把網頁解壓縮,然后正確解碼,解碼的代碼可能是這樣的:

 1 import StringIO, gzip 2  3 f = urllib2.urlopen(url,timeout=10) 4 # consider some html is compressed by server with gzip 5 isGzip = f.headers.get('Content-Encoding') 6 if isGzip: 7     compressedData = f.read() 8     compressedStream = StringIO.StringIO(compressedData) 9     gzipper = gzip.GzipFile(fileobj=compressedStream)10     data = gzipper.read()11 else:12     data = f.read()

  3、調用urlopen函數后程序卡住

  這個的主要問題是沒有設置timeout參數,導致網絡不通暢時引起urlopen函數無限等待,程序卡死,所以,設置timeout=10例如,單位是秒,并處理拋出的socket.timeout異常,就可以避免這個問題了。

  4、被服務器拉黑

  如果你對某個域名訪問速度太快,就可能被服務器認定為潛在的DDos攻擊,IP就會被封一段時間。解決方法是不要過快的訪問可以使用sleep語句參數單位為秒。

import timefor url in urllist:    f = urllib2.urlopen(url,timeout=10)    ...    time.sleep(1)

  (四)小爬蟲

  自己寫了一個小爬蟲,目的是爬取www.gaokao.com網上所有本科大學歷年的分數線等數據,其中schoollist文件存儲了所有大學的ID號,你可以手寫一個list代替例如:

schoollist = ['1','2','3']

  代碼就暴力貼了:

#!/usr/bin/env python# -*- coding: utf-8 -*-__author__ = 'holmes'from HTMLParser import HTMLParserimport urllib2import StringIO, gzipimport threadingimport osimport timeimport sysimport socket# hard codeLOCATION = ("北京", "天津", "遼寧", "吉林", "黑龍江", "上海", "江蘇", "浙江", "安徽", "福建", "山東", "湖北",            "湖南", "廣東", "重慶", "四川", "陜西", "甘肅", "河北", "山西", "內蒙古", "河南", "海南", "廣西",            "貴州", "云南", "西藏", "青海", "寧夏", "新疆", "江西",)# hard codeSUBJECT = ("理科", "文科",)'''Rules for URLhttp://college.gaokao.com/school/tinfo/%d/result/%d/%d/ %(schoolID,localID,subID)where localID from 1 to 31where subID from 1 to 2'''SEED = "http://college.gaokao.com/school/tinfo/%s/result/%s/%s/"SID = "schoolID"  # file name contains school IDsclass SpiderParser(HTMLParser):    def __init__(self, subject=1, location=1):        HTMLParser.__init__(self)        self.campus_name = ""        self.subject = SUBJECT[subject - 1]        self.location = LOCATION[location - 1]        self.table_content = [[], ]        self.line_no = 0        self.__in_h2 = False        self.__in_table = False        self.__in_td = False    def handle_starttag(self, tag, attrs):        if tag == "h2":            self.__in_h2 = True        if tag == "table":            self.__in_table = True        if tag == "tr" and len(attrs) != 0:            if self.__in_table:                self.table_content[self.line_no].append(self.campus_name)                self.table_content[self.line_no].append(self.subject)                self.table_content[self.line_no].append(self.location)        if tag == "td":            if self.__in_table:                self.__in_td = True    def handle_endtag(self, tag):        if tag == "h2":            self.__in_h2 = False        if tag == "table":            self.__in_table = False        if tag == "tr":            if self.__in_table:                self.line_no += 1                self.table_content.append([])        if tag == "td":            if self.__in_table:                self.__in_td = False    def handle_data(self, data):        if self.__in_h2:            self.campus_name = data        if self.__in_td:            self.table_content[self.line_no].append(data)def getschoolID():    with open(SID, mode='r') as rf:        idlist = rf.readlines()        print idlist        return idlistdef gethtml(url):    try:        f = urllib2.urlopen(url,timeout=10)    # consider some html is compressed by server with gzip        isGzip = f.headers.get('Content-Encoding')        if isGzip:            compressedData = f.read()            compressedStream = StringIO.StringIO(compressedData)            gzipper = gzip.GzipFile(fileobj=compressedStream)            data = gzipper.read()        else:            data = f.read()    # decode the html        contentType = f.headers.get('Content-Type')        if contentType.find("gbk"):            data = unicode(data, "GBK").encode("utf-8")        elif contentType.find("utf-8"):            pass    except socket.timeout, e:        data = None        print "time out!"        with open("timeout",'a') as log:            log.write(url+'/n')    finally:        return datadef parseandwrite((slID, lID, sID), wfile):    try:        url = SEED % (slID, lID, sID)        html = gethtml(url)        if html is None:            print "pass a timeout"            return        parser = SpiderParser(sID, lID)        parser.feed(html)        parser.close()        if parser.line_no != 0:            for line in parser.table_content:                for item in line:                    if "--" in item:                        item = "NULL"                    wfile.write(item + ',')                wfile.write('/n')    except urllib2.URLError, e:        print "url error in parseandwrite()"        raisedef thread_task(idlist, name):    try:        print "thread %s is start" % name        wf = open(name, mode='w')        wf.write("大學名稱,文理,省份,年份,最低分,最高分,平均分,錄取人數,錄取批次")        for sID in idlist:            print name + ":%s" % idlist.index(sID)            sID = sID.strip('/n')            i = 1.0            for localID in range(1, 32):                for subID in range(1, 3):                    parseandwrite((sID, localID, subID), wf)                    sys.stdout.write("/rprocess:%.2f%%" % (i / 62.0 * 100))                    sys.stdout.flush()                    i += 1.0                    time.sleep(1)    except urllib2.URLError:        with open("errorlog_" + name, 'w') as f:            f.write("schoolID is %s , locationID is %s ,subID is %s" % (sID, localID, subID))        print "schoolID is %s ,locationID is %s ,subID is %s" % (sID, localID, subID)    finally:        wf.close()THREAD_NO = 1def master():    school = getschoolID()    for i in range(THREAD_NO):        path = os.path.join(os.path.curdir, "errorlog_" + str(i))        if os.path.exists(path):            with open("errorlog_" + str(i), 'r') as rf:                sID = rf.readline().split()[2]                start = school.index(sID)        else:            start = len(school) / THREAD_NO * i        end = len(school) / THREAD_NO * (i + 1) - 1        if i == THREAD_NO - 1:            end = len(school) - 1        t = threading.Thread(target=thread_task, args=(school[start:end], "thread" + str(i),))        t.start()        print "start:%s /n end:%s" % (start, end)    t.join()    print "finish"if __name__ == '__main__':    # thread_task(["1"],"test")    master()    # gethtml("http://www.baidu.com")

 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 玉屏| 江陵县| 钟祥市| 北票市| 柳河县| 灯塔市| 邢台县| 永德县| 无为县| 西吉县| 仲巴县| 黄石市| 合阳县| 佳木斯市| 宁南县| 丰原市| 玉树县| 商洛市| 黄陵县| 仪征市| 安塞县| 黑水县| 沙坪坝区| 南雄市| 泊头市| 岳阳县| 宁南县| 台北县| 淅川县| 鹤庆县| 九龙县| 咸丰县| 桂平市| 嘉禾县| 宜州市| 铜鼓县| 曲麻莱县| 山东省| 都昌县| 芒康县| 富宁县|