【python網路編程】新浪爬蟲:關鍵詞搜尋爬取微博資料__編程

來源:互聯網
上載者:User

    上學期參加了一個大資料比賽,需要抓取大量資料,於是我從新浪微博下手,本來準備使用新浪的API的,無奈新浪並沒有開放關鍵字搜尋的API,所以只能用爬蟲來擷取了。幸運的是,新浪提供了一個進階搜尋功能,為我們爬取資料提供了一個很好的切入點。


        在查閱了一些資料,參考了一些爬蟲的例子後,得到大體思路:構造URL,爬取網頁,然後解析網頁

        具體往下看~

        登陸新浪微博,進入進階搜尋,如圖輸入,之後發送請求會發現地址欄變為如下:    http://s.weibo.com/weibo/%25E4%25B8%25AD%25E5%25B1%25B1%25E5%25A4%25A7%25E5%25AD%25A6&region=custom:44:1&typeall=1&suball=1&timescope=custom:2015-08-07-0:2015-08-08-0&Refer=g        解析如下:
            固定地址部分:http://s.weibo.com/weibo/
            關鍵字二次UTF-8編碼:%25E4%25B8%25AD%25E5%25B1%25B1%25E5%25A4%25A7%25E5%25AD%25A6
            搜尋地區:region=custom:44:1
            搜尋時間範圍:timescope=custom:2015-08-07-0:2015-08-08-0
            可忽略項:Refer=g
            某次請求的頁數:page=1(第一頁可不加)

我們查看一下網頁原始碼看看有什麼鬼:



    小夥伴們第一次看到肯定大呼我的天啊,真的是看的眼花繚亂。

    別著急,讓我娓娓道來。

    首先,我們定位到圖示的地方,即出現字串<script>STK && STK.pageletM && STK.pageletM.view({"pid":"pl_weibo_direct"的地方,此處即搜尋到的微博頁面的代碼啦~
    頁面是unicode碼,所以中文都不能正常顯示~而且上面沒有排版,才顯得如此雜亂。

    我們可以先對抓取到的頁面處理一下,這時就要用到lxml的etree了,它可以將網頁內容的結點構建成一棵樹。

    我們拿出其中一個結點出來看看:

<a class=\"W_texta W_fb\" nick-name=\"\u554a\u5be7\u5504\" href=\"http:\/\/weibo.com\/612364698\" target=\"_blank\" title=\"\u554a\u5be7\u5504\" usercard=\"id=1884932730&usercardkey=weibo_mp\"\t\tsuda-data=\"key=tblog_search_weibo&value=weibo_ss_1_name\" class=\"name_txt W_fb\">

    在這個結點中,我們可以擷取該條微博的博主的一些資訊,如nick-name,微博地址href。

    我們再看看另一個結點:

<p class=\"comment_txt\" node-type=\"feed_list_content\" nick-name=\"\u554a\u5be7\u5504\">\u8fd9\u4e48\u52aa\u529b \u5c45\u7136\u5012\u6570\u7b2c\u4e94 \u5509 \u4e0d\u884c\u6211\u8981\u8ffd\u56de\u6765 \u8d8a\u632b\u8d8a\u52c7 \u4e0d\u53ef\u4ee5\u81ea\u66b4\u81ea\u5f03 \u4e0d\u53ef\u4ee5\u8ba9\u8d1f\u9762\u60c5\u7eea\u8dd1\u51fa\u6765 \u83dc\u575a\u5f3a \u52a0\u6cb9\u52a0\u6cb9\u52a0\u6cb9 \u6211\u8981\u4e0a<em class=\"red\">\u4e2d\u5c71\u5927\u5b66<\/em> \u6211\u8981\u548c\u5c0f\u54c8\u5427\u4e00\u6240\u5927\u5b66 \u62fc\u4e86<\/p>

    這個結點包含的資料即為微博的內容。

    這樣子就清晰很多了。至於如何搜尋相應的結點,取得結點的屬性和內容等,我們用的是xpath這個工具。

    關於xpath,見文 http://blog.csdn.net/raptor/article/details/4516441

    獲得資料後,是資料的儲存,我是將資料匯入到excel中,用到的xlwt和xlrd這兩個模組。

    最後資料的效果(我搜集的資訊比較具體,需要訪問博主的個人首頁擷取,為便於大家閱讀、理解,下面代碼中刪去了這部分):



代碼:

# coding: utf-8'''以關鍵詞收集新浪微博'''import wximport sysimport urllibimport urllib2import reimport jsonimport hashlibimport osimport timefrom datetime import datetimefrom datetime import timedeltaimport randomfrom lxml import etreeimport loggingimport xlwtimport xlrdfrom xlutils.copy import copyclass CollectData():    """資料收集類   利用微博進階搜尋功能,按關鍵字搜集一定時間範圍內的微博。    """    def __init__(self, keyword, startTime, interval='50', flag=True, begin_url_per = "http://s.weibo.com/weibo/"):        self.begin_url_per = begin_url_per  #設定固定地址部分        self.setKeyword(keyword)    #設定關鍵字        self.setStartTimescope(startTime)   #設定搜尋的開始時間        #self.setRegion(region)  #設定搜尋地區        self.setInterval(interval)  #設定鄰近網頁請求之間的基礎時間間隔(注意:過於頻繁會被認為是機器人)        self.setFlag(flag)          self.logger = logging.getLogger('main.CollectData') #初始化日誌    ##設定關鍵字    ##關鍵字需解碼後編碼為utf-8    def setKeyword(self, keyword):        self.keyword = keyword.decode('GBK','ignore').encode("utf-8")        print 'twice encode:',self.getKeyWord()    ##關鍵字需要進行兩次urlencode    def getKeyWord(self):        once = urllib.urlencode({"kw":self.keyword})[3:]        return urllib.urlencode({"kw":once})[3:]    ##設定起始範圍,間隔為1天    ##格式為:yyyy-mm-dd    def setStartTimescope(self, startTime):        if not (startTime == '-'):            self.timescope = startTime + ":" + startTime        else:            self.timescope = '-'    ##設定搜尋地區    #def setRegion(self, region):    #    self.region = region    ##設定鄰近網頁請求之間的基礎時間間隔    def setInterval(self, interval):        self.interval = int(interval)    ##設定是否被認為機器人的標誌。若為False,需要進入頁面,手動輸入驗證碼    def setFlag(self, flag):        self.flag = flag    ##構建URL    def getURL(self):        return self.begin_url_per+self.getKeyWord()+"&typeall=1&suball=1×cope=custom:"+self.timescope+"&page="    ##爬取一次請求中的所有網頁,最多返回50頁    def download(self, url, maxTryNum=4):        hasMore = True  #某次請求可能少於50頁,設定標記,判斷是否還有下一頁        isCaught = False    #某次請求被認為是機器人,設定標記,判斷是否被抓住。抓住後,需要,進入頁面,輸入驗證碼        name_filter = set([])    #過濾重複的微博ID                i = 1   #記錄本次請求所返回的頁數        while hasMore and i < 51 and (not isCaught):    #最多返回50頁,對每頁進行解析,並寫入結果檔案            source_url = url + str(i)   #構建某頁的URL            data = ''   #儲存該頁的網頁資料            goon = True #網路中斷標記            ##網路不好的情況,試著嘗試請求三次            for tryNum in range(maxTryNum):                try:                    html = urllib2.urlopen(source_url, timeout=12)                    data = html.read()                    break                except:                    if tryNum < (maxTryNum-1):                        time.sleep(10)                    else:                        print 'Internet Connect Error!'                        self.logger.error('Internet Connect Error!')                        self.logger.info('url: ' + source_url)                        self.logger.info('fileNum: ' + str(fileNum))                        self.logger.info('page: ' + str(i))                        self.flag = False                        goon = False                        break            if goon:                lines = data.splitlines()                isCaught = True                for line in lines:                    ## 判斷是否有微博內容,出現這一行,則說明沒有被認為是機器人                    if line.startswith('<script>STK && STK.pageletM && STK.pageletM.view({"pid":"pl_weibo_direct"'):                        isCaught = False                        n = line.find('html":"')                        if n > 0:                            j = line[n + 7: -12].encode("utf-8").decode('unicode_escape').encode("utf-8").replace("\\", "")    #去掉所有的\                            ## 沒有更多結果頁面                            if (j.find('<div class="search_noresult">') > 0):                                hasMore = False                            ## 有結果的頁面                            else:                                #此處j要decode,因為上面j被encode成utf-8了                                page = etree.HTML(j.decode('utf-8'))                                ps = page.xpath("//p[@node-type='feed_list_content']")   #使用xpath解析得到微博內容                                addrs = page.xpath("//a[@class='W_texta W_fb']")   #使用xpath解析得到博主地址                                addri = 0                                #擷取暱稱和微博內容                                for p in ps:                                    name = p.attrib.get('nick-name')#擷取暱稱                                    txt = p.xpath('string(.)')#擷取微博內容                                    addr = addrs[addri].attrib.get('href')  #擷取微博地址                                    addri += 1                                    if(name != 'None' and str(txt) != 'None' and name not in name_filter):  #匯出資料到excel中                                        name_filter.add(name)                                        oldWb = xlrd.open_workbook('weiboData.xls', formatting_info=True)                                        oldWs = oldWb.sheet_by_index(0)                                        rows = int(oldWs.cell(0,0).value)                                        newWb = copy(oldWb)                                        newWs = newWb.get_sheet(0)                                        newWs.write(rows, 0, str(rows))                                        newWs.write(rows, 1, name)                                        newWs.write(rows, 2, self.timescope)                                        newWs.write(rows, 3, addr)                                        newWs.write(rows, 4, txt)                                        newWs.write(0, 0, str(rows+1))                                        newWb.save('weiboData.xls')                                        print "save with same name ok"                        break                lines = None                ## 處理被認為是機器人的情況                if isCaught:                    print 'Be Caught!'                    self.logger.error('Be Caught Error!')                    self.logger.info('filePath: ' + savedir)                    self.logger.info('url: ' + source_url)                    self.logger.info('fileNum: ' + str(fileNum))                    self.logger.info('page:' + str(i))                    data = None                    self.flag = False                    break                ## 沒有更多結果,結束該次請求,跳到下一個請求                if not hasMore:                    print 'No More Results!'                    if i == 1:                        time.sleep(random.randint(3,8))                    else:                        time.sleep(10)                    data = None                    break                i += 1                ## 設定兩個鄰近URL請求之間的隨機休眠時間,防止Be Caught                sleeptime_one = random.randint(self.interval-25,self.interval-15)                sleeptime_two = random.randint(self.interval-15,self.interval)                if i%2 == 0:                    sleeptime = sleeptime_two                else:                    sleeptime = sleeptime_one                print 'sleeping ' + str(sleeptime) + ' seconds...'                time.sleep(sleeptime)            else:                break    ##改變搜尋的時間範圍,有利於擷取最多的資料       def getTimescope(self, perTimescope):        if not (perTimescope=='-'):            times_list = perTimescope.split(':')            start_date =  datetime(int(times_list[-1][0:4]),  int(times_list[-1][5:7]), int(times_list[-1][8:10]) )             start_new_date = start_date + timedelta(days = 1)            start_str = start_new_date.strftime("%Y-%m-%d")            return start_str + ":" + start_str        else:            return '-'def main():    logger = logging.getLogger('main')    logFile = './collect.log'    logger.setLevel(logging.DEBUG)    filehandler = logging.FileHandler(logFile)    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s: %(message)s')    filehandler.setFormatter(formatter)    logger.addHandler(filehandler)    while True:        ## 接受鍵盤輸入        keyword = raw_input('Enter the keyword(type \'quit\' to exit ):')        if keyword == 'quit':            sys.exit()        startTime = raw_input('Enter the start time(Format:YYYY-mm-dd):')        #region = raw_input('Enter the region([BJ]11:1000,[SH]31:1000,[GZ]44:1,[CD]51:1):')        interval = raw_input('Enter the time interval( >30 and deafult:50):')        ##執行個體化收集類,收集指定關鍵字和起始時間的微博        cd = CollectData(keyword, startTime, interval)        while cd.flag:            print cd.timescope            logger.info(cd.timescope)            url = cd.getURL()            cd.download(url)            cd.timescope = cd.getTimescope(cd.timescope)  #改變搜尋的時間,到下一天        else:            cd = None            print '-----------------------------------------------------'            print '-----------------------------------------------------'    else:        logger.removeHandler(filehandler)        logger = None##if __name__ == '__main__':##    main()

上面實現了資料的爬取,再結合上一篇文章中的類比登入,就可以美美的抓資料啦~


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.