【Python網絡爬蟲】百度貼吧/豆瓣小組

爲什麼我要做爬蟲

其實做爬蟲已經有幾次經歷了,但從來沒有把爬蟲的相關工作做過總結,所以在我第三次寫爬蟲卻還要網頁搜索具體寫法的時候,我決定還是自己把爬蟲的相關技術記錄下來。

基礎版爬蟲

最基礎版的爬蟲很簡單,我們都知道爬蟲其實就只是要把網頁信息獲取,也就是你右鍵Ctrl+U看到的那堆源代碼,然後按照自己所需要的利用正則匹配出你要摳的內容就好了,所以最基礎版的爬蟲作用就在於此。
人生苦短,我們用Python。Python作爲世界上我最喜歡的編程語言,也有很強大的網絡工具庫,在Python2.7中有利器urllib2來幫助我們實現快速爬蟲。

import urllib2
response = urllib2.urlopen('http://www.douban.com/')
html = response.read()
print html

爬蟲的兩種方式:

  • 深度優先:先爬取該網頁的所有鏈接再把這些鏈接中的所有鏈接都獲取了,最後在爬內容;
  • 廣度優先:先爬取某網頁的鏈接,把該網頁爬完了再去爬下一個網頁的內容及裏面的鏈接

僞裝成瀏覽器進行爬蟲

爲什麼需要僞裝

因爲好多網頁都設置了反爬蟲=。=一旦被發現是機器而不是人的話就被被禁止訪問或者禁號的。

怎麼僞裝

服務器一般確定到底是機器還是人在操作是看發送數據的時候發送的信息裏有沒有他想要查的數據,比如User-Agent,比如Header。其中這兩項也是最重要的兩項,下面介紹如何獲取。
- 如何獲取瀏覽器headers信息
採用debug的方式可以將爬取的信息

# /usr/bin/env python
# -*-coding:utf-8 -*

import urllib2

httpHandler = urllib2.HTTPHandler(debuglevel=1)
httpsHandler = urllib2.HTTPSHandler(Idebuglevel=1)
opener = urllib2.urlopen(httpHandler,httpsHandler)
urllib2.install_opener(opener)
response = urllib2.urlopen('http://www.douban.com')     

最後打印出的信息如下:
這裏寫圖片描述

  • 通過瀏覽器獲取
    很簡單瀏覽器上F12,然後點擊Network,刷新一下界面就可以看到你給服務器發了什麼信息,服務器返回了什麼信息。

    這裏寫圖片描述

注意我上面黑色拉出來的一段就是需要的User-Agent信息

  • 僞裝之後需要做什麼
    爬網頁+正則神器
    關於正則表達式的內容不在這個地方贅述了,下次單獨寫一篇關於正則表達式的內容。

爬蟲可以使用的小tricks

  • 採用sleep減慢你爬蟲的速度,雖然會相對減慢速度,但這真的好過你一次一次的被封號又重新open你的程序
  • 更絕一點就直接把sleep的參數設置爲一個隨機數=。=
  • 儘量爬一個網頁就保存到文本里,要不你會後悔的你真的會後悔的。別問我怎麼知道~

最後,給出兩個我在項目中用的代碼,其實對於爬蟲而言最有特點的是每個網站都不一樣,所以在你確定要爬蟲之前最好先解析一下網頁的源代碼,看看好不好爬,That’s it!


附錄

百度貼吧爬蟲源代碼

# /usr/bin/env python
# -*-coding:utf-8 -*-
import socket
import string
import re
import urllib2

class HTML_Tool:
    # 用非 貪婪模式 匹配 \t 或者 \n 或者 空格 或者 超鏈接 或者 圖片
    BgnCharToNoneRex = re.compile("(\t|\n| |<a.*?>|<img.*?>)")

    # 用非 貪婪模式 匹配 任意<>標籤
    EndCharToNoneRex = re.compile("<.*?>")

    # 用非 貪婪模式 匹配 任意<p>標籤
    BgnPartRex = re.compile("<p.*?>")
    CharToNewLineRex = re.compile("(<br/>|</p>|<tr>|<div>|</div>)")
    CharToNextTabRex = re.compile("<td>")

    # 將一些html的符號實體轉變爲原始符號
    replaceTab = [("&lt;","<"),("&gt;",">"),("&amp;","&"),("&amp;","\""),("&nbsp;"," ")]

    def Replace_Char(self,x):
        x = self.BgnCharToNoneRex.sub("",x)
        x = self.BgnPartRex.sub("\n    ",x)
        x = self.CharToNewLineRex.sub("\n",x)
        x = self.CharToNextTabRex.sub("\t",x)
        x = self.EndCharToNoneRex.sub("",x)

        for t in self.replaceTab:
            x = x.replace(t[0],t[1])
        return x

class Baidu_Spider:
    # 申明相關的屬性
    def __init__(self,url):
        self.myUrl = url + '?see_lz=0'
        self.datas = []

        self.myTool = HTML_Tool()
        print u'已經啓動百度貼吧爬蟲,咔嚓咔嚓'

    # 初始化加載頁面並將其轉碼儲存
    def baidu_tieba(self):
        try:
            # 讀取頁面的原始信息並將其從utf8轉碼
            myPage = urllib2.urlopen(self.myUrl).read().decode('utf-8')
            # 計算樓主發佈內容一共有多少頁
            endPage = self.page_counter(myPage)
            # 獲取最終的數據

            self.save_data(self.myUrl,endPage)
        except UnicodeDecodeError,urllib2.URLError:
            print u'不小心解碼出錯了,我直接跳到下一個可以麼~'
            pass

    #用來計算一共有多少頁
    def page_counter(self,myPage):
        # 匹配 "共有<span class="red">12</span>頁" 來獲取一共有多少頁
        myMatch = re.search(r'class="red">(\d+?)</span>', myPage, re.S)
        if myMatch:
            endPage = int(myMatch.group(1))
            print u'爬蟲報告:發現共有%d頁的原創內容' % endPage
        else:
            endPage = 0
            print u'爬蟲報告:無法計算有多少頁!'
        return endPage


    def save_data(self,url,endPage):
        # 加載頁面數據到數組中
        self.get_data(url,endPage)
        # 打開本地文件
        f = open('baidu.06-16','a')
        f.writelines(self.datas)
        print u'爬蟲報告:文件已下載到本地並打包成txt文件'
        f.close()
        #f.close()


    # 獲取頁面源碼並將其存儲到數組中
    def get_data(self,url,endPage):
        url = url + '&pn='
        for i in range(1,endPage+1):
            print u'爬蟲報告:爬蟲%d號正在加載中...' % i
            myPage = urllib2.urlopen(url + str(i),timeout=30).read()
            # 將myPage中的html代碼處理並存儲到datas裏面
            self.deal_data(myPage.decode('utf-8'))


    # 將內容從頁面代碼中摳出來
    def deal_data(self,myPage):
        myItems = re.findall('id="post_content.*?>(.*?)</div>',myPage,re.S)
        for item in myItems:
            data = self.myTool.Replace_Char(item.replace("\n","").encode('utf-8'))
            self.datas.append(data+'\n')


##獲取當前地址的內容
def get_current_content(url):
    print u'已開始啓動爬蟲...'
    print u'要爬的吧是:'+url
    content  =  urllib2.urlopen(url,timeout=30).read().decode('utf8')
    print u'網頁信息已全部獲取'
    return content

##獲取下一頁的地址
def get_next_href(current_content):
    #格式爲<a href="/f/909090" class="next">
    #匹配該模式,獲取下一頁的地址
    next = re.compile(r'href="(\S*)"[^><]* class="next"')
    next_page =  re.findall(next,current_content)
    #判斷下一頁是否存在
    if (next_page is []):
        print u'我擦嘞,累死老孃了,終於完了啊你~~~'
        return False
    else:
        next_page_url='http://tieba.baidu.com'+str(next_page[0])
        print u'怎麼還有啊,累死姐了...'
        return next_page_url

##獲取當前頁面的所有href及其內容
def get_current_href_content(content,title):
    #格式爲<a href="/p/2020020200" title = "" target ="_blank" class = "j_th_tit" >
    #匹配該模式,獲取href
    match = re.compile(r'href="(\S*)"[^><]*target="_blank" class="j_th_tit"')
    list= re.findall(match,content)
    #把鏈接地址補全
    for item in list:
        item=item.replace(item,'http://tieba.baidu.com'+item)
        print item
        ##對每一個href進行爬蟲
        mySpider = Baidu_Spider(item)
        mySpider.baidu_tieba()

def find_title(content):
        # 匹配 <h1 class="core_title_txt" title="">xxxxxxxxxx</h1> 找出標題
        myMatch = re.search(r'<title>(.*?)</title>', content, re.S)
        title = u'暫無標題'
        if myMatch:
            title  = myMatch.group(1)
        else:
            print u'爬蟲報告:無法加載文章標題!'
        # 文件名不能包含以下字符: \ / : * ? " < > |
        title = title.replace('\\','').replace('/','').replace(':','').replace('*','').replace('?','').replace('"','').replace('>','').replace('<','').replace('|','')
        return title


#------------------------------程序入口--------------------------------#

#用戶選擇要爬的吧,輸入這個吧的網址
print u"""
#--------------------------------------------------------------------
#   程序:百度貼吧爬蟲
#   作者:memory
#   日期:2014-03-27
#   語言:Python 2.7
#   功能:輸入百度貼吧中某個吧的網址之後將本吧的所有內容都提取下來保存爲TXT文件
#------------------------------------------------------------------------------
"""

print u'請輸入貼吧首頁地址...'
myurl =  [
        'http://tieba.baidu.com/f?kw=nba',
        'http://tieba.baidu.com/f?kw=%BB%F0%BC%FD',
        'http://tieba.baidu.com/f?kw=%C2%F3%B5%CF'
        ]
#print u'請輸入貼吧名字'
for item in myurl:
#獲取貼吧首頁地址
#獲取下一頁的地址、每一個頁面所有鏈接的地址及其內容。並返回下一頁的地址
#獲取當前網站的所有頁面代碼

    url = item
    content=get_current_content(url)
    #獲取當前頁面的所有href
    title = find_title(content)
    print title
    while(url is not False):
        try:

            content=get_current_content(url)
            #獲取當前頁面的所有href
            href=get_current_href_content(content,title)
            #獲取當前頁的下一頁鏈接地址
            url=get_next_href(content)

        except:
            print u'啊啊啊啊,我要死了。。。真的不行了~'
            print url
            break

豆瓣小組爬蟲源代碼

#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright © 2015 memoryfeng     Huazhong University of Science and Technology
#
# Distributed under terms of the MIT license.

"""

"""

import sys
import argparse
import socket
import string
import re
import urllib2
import time
hds={'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36','Server':'dae','Content-Type':'text/html;charset=utf-8','Connection':'close',\
        'X-Douban-Mobileapp':'0','Expires':'Sun, 1 Jan 2006 01:00:00 GMT',\
        'X-Douban-Newbid':'qNL7av2zyKI','Pragma':'no-cache','Cache-Control':'t-revalidate, no-cache, private',\
        'P3P' : 'CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"',\
        'X-Douban-Splittest':'Set-Cookied="qNL7av2zyKI"; path=/; domain=.douban.com; expires=Wed, 23-Nov-2016 01:48:13 GMT',\
        'Set-Cookie':'th=/; domain=.douban.com; expires=Wed, 23-Nov-2016 01:48:13 GMT',\
        'X-DAE-Node':'sindar10b'}
def get_url(url):
    # Get all the urls in this page.
    # Get content here
    #print url
    req = urllib2.Request(url,None,hds)
    data = urllib2.urlopen(req).read()
    #data = urllib2.urlopen(url, timeout=30).read()
    #print data
    # Match the urls.Style:<h3><a href="xxx"></a></h3>
    urls = re.findall('<a href="http://www.douban.com/group/topic/(\d*)/">', data,re.S)
    return urls


def writetxt(lst):
    f = open('doubangroup.1124','a')
    for item in lst:
        f.writelines(item+'\n')
    f.close()

def get_content(data):
    title = re.findall('<span class="topic-figure-title">(\S*?)</span>', data,re.S)
    comments = re.findall('<p class="">(\S*)</p>',data,re.S)
    content = re.findall('<p>(\S*)</p>',data,re.S)
    writetxt(title)
    writetxt(comments)
    writetxt(content)
    time.sleep(12)

if __name__=="__main__":

    print " Crawler for Douban Group comments.\n"
    print " Mission Recieved.\n"
    print " Spidering now...\n"

    #header = {'User-Agent':'Mozilla/5.0'}
    dburl = "http://www.douban.com/group/explore/ent?start="
    for page in range(0, 268):
        print page
        url = dburl + str(page*30)
        # get all the url in this page
        current_urls = get_url(url)
        # get all the content & the comments in this topic page
        for url in current_urls:
            req = urllib2.Request('http://www.douban.com/group/topic/'+url+'/',None,hds)
            #data = urllib2.urlopen('http://www.douban.com/group/topic/'+url+'/',timeout=30).read()
            data = urllib2.urlopen(req).read()
            get_content(data)
            # write the content into the text file
    print " I have got all the contents.\n"
    print " Say thank you to me~\n"
    print " Try again!"

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章