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爬糗百