1. 程式人生 > >python3.x之爬蟲學習

python3.x之爬蟲學習

首先需要知道python3.x中urllib.request是用於開啟URL的可擴充套件庫。
一。 1.最簡單的爬蟲就是把整個網頁儲存到本地分為如下幾步
①.訪問url
②.讀取網頁
③.儲存網頁
實現程式碼:

#encoding:UTF-8
from urllib.request import urlopen
import os
def main():
    url="http://www.python.org"
    f = urlopen(url)#1
    data = f.read().decode("utf-8")#2
    f.close()
    filename="index.html"
f=open(filename,'w')#3 f.write(data) f.close() #print(data)#輸出網頁資訊 print(type(f)) print(f.geturl())#返回檢索的資源的URL,通常用於確定是否遵循重定向 print(f.info())#以email.message_from_string()例項的形式返回頁面的元資訊(如標題)(請參閱 HTTP頭快速參考) print(f.getcode())#返回響應的HTTP狀態程式碼 if __name__ == '__main__': main()

2.查詢可變網址(例如百度搜索)
首先需要了解urllib.parse.urlencode()

import urllib.request
data={}
data['word']='songqiu65'

url_values=urllib.parse.urlencode(data)
url="http://www.baidu.com/s?"
full_url=url+url_values#合併為完整網址

data=urllib.request.urlopen(full_url).read()
data=data.decode('UTF-8')
print(data)

3.偽裝成瀏覽器來爬網頁


有些網站需要瀏覽器發起訪問,我們就需要新增報頭
少:

import urllib.request
weburl = 'http://www.baidu.com/'
webheader = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0'}
req = urllib.request.Request(url=weburl, headers=webheader)
webPage=urllib.request.urlopen(req)
data = webPage.read()
data = data.decode('UTF-8')
print(data)
print(type(webPage))
print(webPage.geturl())
print(webPage.info())
print(webPage.getcode())

多:

import urllib.request
weburl = 'http://www.baidu.com/'
webheader = {
    'Connection': 'Keep-Alive',
    'Accept': 'text/html, application/xhtml+xml, */*',
    'Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3',
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko',
    'Host': 'www.baidu.com',
    'DNT': '1'
    }
req = urllib.request.Request(url=weburl, headers=webheader)
webPage=urllib.request.urlopen(req)
data = webPage.read()
data = data.decode('UTF-8')
print(data)
print(type(webPage))
print(webPage.geturl())
print(webPage.info())
print(webPage.getcode())

二。1.爬單頁面圖片(需要正則表示式)

#encoding:utf-8
import urllib.request
import re
import os
def buildcdw():
    try:
        targetDir = os.getcwd()  # 檔案儲存路徑
        os.chdir(targetDir)
        try:
            os.mkdir('pic')
            print("建立目錄pic")
        except:
            print(targetDir+"\pic目錄已存在,將直接存到該目錄")
        finally:
            os.chdir("pic")
        targetDir = os.getcwd()
    except :
        print("error")
        os._exit()
    return targetDir

def destFile(x,pat):
    pos = pat.rindex('/')#獲取'/'最後出現的位置
    t = os.path.join(x, pat[pos+1:])#path[pos+1:]提取連結中的檔名
    return t
if __name__ == "__main__":  #程式執行入口
    weburl = 'http://www.douban.com/'
    webheaders = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0'}
    req = urllib.request.Request(url=weburl, headers=webheaders)  #構造請求報頭
    webpage = urllib.request.urlopen(req)  #傳送請求報頭
    contentBytes = webpage.read()
    x=buildcdw()
    for link, t in set(re.findall(r'(https:[^s]*?(jpg|png|gif))', str(contentBytes))):  #正則表示式查詢所有的圖片

        print(link+"\t"+t)
        try:
            urllib.request.urlretrieve(link, destFile(x,link)) #下載圖片
        except:
            print('失敗') #異常丟擲

2.解析頁面連結

from html.parser import HTMLParser
from bs4 import BeautifulSoup
import io
from urllib.request import urlopen
from urllib.parse import urljoin
URLS = (
    'http://python.org',
)

def output(x):
    print ('\n'.join(sorted(set(x))))

def simpleBS(url, f):
    'simpleBS() - use BeautifulSoup to parse all tags to get anchors'
    output(urljoin(url, x['href']) for x in BeautifulSoup(
        f,"html5lib").findAll('a'))

def htmlparser(url, f):
    'htmlparser() - use HTMLParser to parse anchor tags'
    class AnchorParser(HTMLParser):
        def handle_starttag(self, tag, attrs):
            if tag != 'a':
                return
            if not hasattr(self, 'data'):
                self.data = []
            for attr in attrs:
                if attr[0] == 'href':
                    self.data.append(attr[1])
    parser = AnchorParser()
    parser.feed(f.read())
    output(urljoin(url, x) for x in parser.data)

def process(url, data):
    print ('\n*** simple BS')
    simpleBS(url, data)
    data.seek(0)
    print ('\n*** HTMLParser')
    htmlparser(url, data)

def main():
    for url in URLS:
        f = urlopen(url)
        data = io.StringIO(f.read().decode("utf-8"))
        f.close()
        process(url, data)

if __name__ == '__main__':
    main()

上面程式碼裡面有兩種方式用BeautifulSoup或者HTMLParser,用的不同的庫,若缺少,請點選下載。
三。1.模擬登陸知乎
需要用到PIL可以cmd執行pip install pillow==3.4.2
寫這個時,手裡只有一份沒有獲取驗證碼的程式碼。看著程式碼也看不懂,就用了作者推薦的fiddler軟體,自己一步一步去看網頁原始碼尋找答案,例如get和post的資訊,_xsrf的獲取,主要是驗證碼問題(原始碼是沒有的)。驗證碼有兩種一種是圖片輸入字元型別,一類是點選型別。輸入字元我可以下載圖片到本地通過圖片庫顯示出來,然後再手動輸入;點選型別沒研究。其次是獲取驗證碼來源,網頁原始碼是沒有的,通過f12或者fd軟體可以發現它始終有部分固定,變化的是https://www.zhihu.com/captcha.gif?r="+t+"&type=login中間的t。這裡我是自己沒有解決辦法的,就只能搜尋了。搜尋答案t是時間。

程式碼:

#coding:utf-8
"""手機號登入知乎 r=1失敗0成功"""
import gzip
import re
import sys
import http.cookiejar
import urllib.request
import urllib.parse
from PIL import Image
import io
import time


# 構造header,一般header至少要包含一下兩項。這兩項是從抓到的包裡分析得出的。
header = {
    'Connection': 'Keep-Alive',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
    'Upgrade-Insecure-Requests': '1',
    'Accept-Language': 'zh-CN,zh;q=0.8',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36',
    'Host': 'www.zhihu.com',
    'DNT': '1'
}

def getyzm(data,i):#獲取驗證碼
    cer = re.compile("captcha-module\" data-type=\"en\"", flags=0)
    str1 = cer.search(data)
    if(str1):
        t = str(int(time.time()*1000))
        t= "https://www.zhihu.com/captcha.gif?r="+t+"&type=login"
        file = opener.open(t)
        file = io.BytesIO(file.read())
        im = Image.open(file)
        im.show()
        x=input("請輸入圖片中的字元:")
        return x
    else:
        if(i<10):
            print("驗證碼拉取失敗,重新拉取中!")
            i+=1
            getyzm(data,i)
        else:
            print("無法拉去圖片驗證碼,即將退出!")
            sys.exit(0)

# 解壓函式
def ungzip(data):
    try:  # 嘗試解壓
        print('正在解壓.....')
        data = gzip.decompress(data)
        print('解壓完畢!')
    except:
        print('未經壓縮, 無需解壓')
    return data


# 獲取_xsrf
def getXSRF(data):
    cer = re.compile("name=\"_xsrf\" value=\"(.*)\"", flags=0)
    strlist = cer.findall(data)
    return strlist[0]


# 構造檔案頭
def getOpener(head):
    # 設定一個cookie處理器,它負責從伺服器下載cookie到本地,並且在傳送請求時帶上本地的cookie
    cj = http.cookiejar.CookieJar()
    pro = urllib.request.HTTPCookieProcessor(cj)
    opener = urllib.request.build_opener(pro)
    header = []
    for key, value in head.items():
        elem = (key, value)
        header.append(elem)
    opener.addheaders = header
    return opener

def getIdPass():
    i=input("請輸入知乎手機號:\n")
    p=input("請輸入知乎密碼:\n")
    return (i,p)

def main():
    global opener

    #獲取驗證碼和_xsrf
    url = 'https://www.zhihu.com/'
    opener = getOpener(header)
    op = opener.open(url)
    data = op.read()
    data = ungzip(data.decode('utf-8'))  # 解壓
    yzm=getyzm(data,0)
    _xsrf = getXSRF(data)

    # 獲取賬號密碼
    id,password = getIdPass()

    # 構造Post資料,從抓大的包裡分析得出的。
    postDict = {
        '_xsrf': _xsrf,  # 特有資料,不同網站可能不同
        'phone_num': id,
        'password': password,
        'captcha':yzm
    }
    # 需要給Post資料編碼
    url='https://www.zhihu.com/login/phone_num'
    postData = urllib.parse.urlencode(postDict).encode()
    op = opener.open(url, postData)
    data = op.read()
    data = ungzip(data.decode("utf-8"))
    print(data)

if __name__=="__main__":
    main()

2.爬取糗事段子
程式碼有缺陷,就是在爬取段子時,會從沒有神評的段子到最先出現神評的段子都是沒有神評的。
由於第一個文字頁面網址為http://www.qiushibaike.com/text/,第2及以後的頁面為http://www.qiushibaike.com/text/page/2/?s=4971603,出現了頁面數和s,所以有函式getID獲取s

程式碼:

#coding:utf-8
"""獲取糗事百科文字糗事(有神評版)"""
import re
import urllib.parse
import urllib.request
# 構造header
header = {
    'Connection': 'Keep-Alive',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
    'Upgrade-Insecure-Requests': '1',
    'Accept-Language': 'zh-CN,zh;q=0.8',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36',
    'Host': 'www.qiushibaike.com',
    'DNT': '1'
}
url = 'http://www.qiushibaike.com/text/'
def simpleBS(f):#匹配並輸出
    i=1
    x = re.compile("<a href=\"/users/([0-9]{8})/\" target=\"_blank\" title=\"(.+)\">([\s\S]*?)<span>(.*)</span>([\s\S]*?)<i class=\"number\">(\d+)</i> 好笑</span>([\s\S]*?)<i class=\"number\">(\d+)</i> 評論([\s\S]*?)<div class=\"single-share\">(([\s\S]*?)<span class=\"cmt-name\">(.+)</span>(\n*)<div class=\"main-text\">(\n*)(.+)(\n*)<div class=\"likenum\">){0,1}", flags=0)
    for y in x.findall(f):
        if("<a href=\"/users/" not in y[10]):
            print(("第{0}條:"+"id:" + y[0] + "\t" + "name:" + y[1] + "\t"+"笑臉:"+y[5]+"\t"+"評論數:"+y[7]+"\n內容:\n"+y[3].replace("<br/>","\n")+"\n"+"神評者:"+y[11]+"\t"+"神評"+y[14]+"\n\n").format(i))
            i += 1
        else:
            x1=re.compile("<a href=\"/users/([0-9]{8})/\" target=\"_blank\" title=\"(.+)\">([\s\S]*?)<span>(.*)</span>([\s\S]*?)<i class=\"number\">(\d+)</i> 好笑</span>([\s\S]*?)<i class=\"number\">(\d+)</i> 評論([\s\S]*?)<div class=\"single-share\">", flags=0)
            for z in x1.findall(y[10]):
                print(("第{0}條:" + "id:" + z[0] + "\t" + "name:" + z[1] + "\t" + "笑臉:" + z[5] + "\t" + "評論數:" + z[7] + "\n內容:\n" + z[3].replace("<br/>", "\n") + "\n\n").format(i))
                i+=1

def getnew(i,ii):#獲取糗事
    urlnew=url+"page/"+str(i)+"/?s="+str(ii)
    op=urllib.request.Request(urlnew,None,header)
    op=urllib.request.urlopen(op)
    data=op.read().decode("utf-8")
    #print(data)
    simpleBS(data)

def getID():#獲取連結中的數字部分
    op = urllib.request.Request(url, None, header)
    op = urllib.request.urlopen(op)
    data = op.read().decode("utf-8")
    x=re.compile("s = (\d+)\" rel=\"nofollow\">")
    y=x.findall(data)
    return y

if __name__=="__main__":
    i1=int(input("你需要從第幾頁開始的糗事:"))
    i2 = int(input("你需要到第幾頁結束的糗事:"))
    i2+=1
    i3=getID()
    if(i1<i2):
        for ii in range(i1,i2):
            print("第"+str(ii)+"頁:\n")
            getnew(ii,i3)


這裡附scrapy爬糗百