程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

python爬蟲

編輯:Python

目錄

概念

1、什麼是爬蟲

2. 瀏覽網頁的過程

3.URL 的含義

4. 環境的配置

安裝

引入

基本請求

基本 GET 請求

基本 POST 請求

Cookies

超時配置

會話對象

SSL 證書驗證

代理

 實戰

完整代碼


概念

1、什麼是爬蟲

爬蟲,即網絡爬蟲,大家可以理解為在網絡上爬行的一直蜘蛛,互聯網就比作一張大網,而爬蟲便是在這張網上爬來爬去的蜘蛛咯,如果它遇到資源,那麼它就會抓取下來。想抓取什麼?這個由你來控制它咯。 比如它在抓取一個網頁,在這個網中他發現了一條道路,其實就是指向網頁的超鏈接,那麼它就可以爬到另一張網上來獲取數據。這樣,整個連在一起的大網對這之蜘蛛來說觸手可及,分分鐘爬下來不是事兒。

2. 瀏覽網頁的過程

在用戶瀏覽網頁的過程中,我們可能會看到許多好看的圖片,比如 百度圖片-發現多彩世界 ,我們會看到幾張的圖片以及百度搜索框,這個過程其實就是用戶輸入網址之後,經過 DNS 服務器,找到服務器主機,向服務器發出一個請求,服務器經過解析之後,發送給用戶的瀏覽器 HTML、JS、CSS 等文件,瀏覽器解析出來,用戶便可以看到形形色色的圖片了。 因此,用戶看到的網頁實質是由 HTML 代碼構成的,爬蟲爬來的便是這些內容,通過分析和過濾這些 HTML 代碼,實現對圖片、文字等資源的獲取。

3.URL 的含義

URL,即統一資源定比特符,也就是我們說的網址,統一資源定比特符是對可以從互聯網上得到的資源的比特置和訪問方法的一種簡潔的錶示,是互聯網上標准資源的地址。互聯網上的每個文件都有一個唯一的 URL,它包含的信息指出文件的比特置以及瀏覽器應該怎麼處理它。

URL 的格式由三部分組成: ①第一部分是協議 (或稱為服務方式)。 ②第二部分是存有該資源的主機 IP 地址 (有時也包括端口號)。 ③第三部分是主機資源的具體地址,如目錄和文件名等。

爬蟲爬取數據時必須要有一個目標的 URL 才可以獲取數據,因此,它是爬蟲獲取數據的基本依據,准確理解它的含義對爬蟲學習有很大幫助。

4. 環境的配置

學習 Python,當然少不了環境的配置,最初我用的是 Notepad++,不過發現它的提示功能實在是太弱了,於是,在 Windows 下我用了 PyCharm,在 Linux 下我用了 Eclipse for Python。

安裝

利用 pip 安裝

1
$ pip install requests

或者利用 easy_install

1
$ easy_install requests

通過以上兩種方法均可以完成安裝。

引入

首先我們引入一個小例子來感受一下

1
2
3
4
5
6
7
8
import requests
r = requests.get('http://cuiqingcai.com')
print type(r)
print r.status_code
print r.encoding
#print r.text
print r.cookies

以上代碼我們請求了本站點的網址,然後打印出了返回結果的類型,狀態碼,編碼方式,Cookies 等內容。 運行結果如下

1
2
3
4
<class 'requests.models.Response'>
200
UTF-8
<RequestsCookieJar[]>

怎樣,是不是很方便。別急,更方便的在後面呢。

基本請求

requests 庫提供了 http 所有的基本請求方式。例如

1
2
3
4
5
r = requests.post("http://httpbin.org/post")
r = requests.put("http://httpbin.org/put")
r = requests.delete("http://httpbin.org/delete")
r = requests.head("http://httpbin.org/get")
r = requests.options("http://httpbin.org/get")

嗯,一句話搞定。

基本 GET 請求

最基本的 GET 請求可以直接用 get 方法

1
r = requests.get("http://httpbin.org/get")

如果想要加參數,可以利用 params 參數

1
2
3
4
5
import requests
payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.get("http://httpbin.org/get", params=payload)
print r.url

運行結果

1
http://httpbin.org/get?key2=value2&key1=value1

如果想請求 JSON 文件,可以利用 json () 方法解析 例如自己寫一個 JSON 文件命名為 a.json,內容如下

1
2
3
["foo", "bar", {
"foo": "bar"
}]

利用如下程序請求並解析

1
2
3
4
5
import requests
r = requests.get("a.json")
print r.text
print r.json()

運行結果如下,其中一個是直接輸出內容,另外一個方法是利用 json () 方法解析,感受下它們的不同

1
2
3
4
["foo", "bar", {
"foo": "bar"
}]
[u'foo', u'bar', {u'foo': u'bar'}]

如果想獲取來自服務器的原始套接字響應,可以取得 r.raw 。 不過需要在初始請求中設置 stream=True 。

1
2
3
4
5
r = requests.get('https://github.com/timeline.json', stream=True)
r.raw
<requests.packages.urllib3.response.HTTPResponse object at 0x101194810>
r.raw.read(10)
'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'

這樣就獲取了網頁原始套接字內容。 如果想添加 headers,可以傳 headers 參數

1
2
3
4
5
6
import requests
payload = {'key1': 'value1', 'key2': 'value2'}
headers = {'content-type': 'application/json'}
r = requests.get("http://httpbin.org/get", params=payload, headers=headers)
print r.url

通過 headers 參數可以增加請求頭中的 headers 信息

基本 POST 請求

對於 POST 請求來說,我們一般需要為它增加一些參數。那麼最基本的傳參方法可以利用 data 這個參數。

1
2
3
4
5
import requests
payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.post("http://httpbin.org/post", data=payload)
print r.text

運行結果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"args": {},
"data": "",
"files": {},
"form": {
"key1": "value1",
"key2": "value2"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "23",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.9.1"
},
"json": null,
"url": "http://httpbin.org/post"
}

可以看到參數傳成功了,然後服務器返回了我們傳的數據。 有時候我們需要傳送的信息不是錶單形式的,需要我們傳 JSON 格式的數據過去,所以我們可以用 json.dumps () 方法把錶單數據序列化。

1
2
3
4
5
6
7
import json
import requests
url = 'http://httpbin.org/post'
payload = {'some': 'data'}
r = requests.post(url, data=json.dumps(payload))
print r.text

運行結果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"args": {},
"data": "{\"some\": \"data\"}",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "16",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.9.1"
},
"json": {
"some": "data"
},
"url": "http://httpbin.org/post"
}

通過上述方法,我們可以 POST JSON 格式的數據 如果想要上傳文件,那麼直接用 file 參數即可 新建一個 a.txt 的文件,內容寫上 Hello World!

1
2
3
4
5
6
import requests
url = 'http://httpbin.org/post'
files = {'file': open('test.txt', 'rb')}
r = requests.post(url, files=files)
print r.text

可以看到運行結果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"args": {},
"data": "",
"files": {
"file": "Hello World!"
},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "156",
"Content-Type": "multipart/form-data; boundary=7d8eb5ff99a04c11bb3e862ce78d7000",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.9.1"
},
"json": null,
"url": "http://httpbin.org/post"
}

這樣我們便成功完成了一個文件的上傳。 requests 是支持流式上傳的,這允許你發送大的數據流或文件而無需先把它們讀入內存。要使用流式上傳,僅需為你的請求體提供一個類文件對象即可

1
2
with open('massive-body') as f:
requests.post('http://some.url/streamed', data=f)

這是一個非常實用方便的功能。

Cookies

如果一個響應中包含了 cookie,那麼我們可以利用 cookies 變量來拿到

1
2
3
4
5
6
import requests
url = 'http://example.com'
r = requests.get(url)
print r.cookies
print r.cookies['example_cookie_name']

以上程序僅是樣例,可以用 cookies 變量來得到站點的 cookies 另外可以利用 cookies 變量來向服務器發送 cookies 信息

1
2
3
4
5
6
import requests
url = 'http://httpbin.org/cookies'
cookies = dict(cookies_are='working')
r = requests.get(url, cookies=cookies)
print r.text

運行結果

1
'{"cookies": {"cookies_are": "working"}}'

可以已經成功向服務器發送了 cookies

超時配置

可以利用 timeout 變量來配置最大請求時間

1
requests.get('http://github.com', timeout=0.001)

注:timeout 僅對連接過程有效,與響應體的下載無關。 也就是說,這個時間只限制請求的時間。即使返回的 response 包含很大內容,下載需要一定時間,然而這並沒有什麼卵用。

會話對象

在以上的請求中,每次請求其實都相當於發起了一個新的請求。也就是相當於我們每個請求都用了不同的瀏覽器單獨打開的效果。也就是它並不是指的一個會話,即使請求的是同一個網址。比如

1
2
3
4
5
import requests
requests.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
r = requests.get("http://httpbin.org/cookies")
print(r.text)

結果是

1
2
3
{
"cookies": {}
}

很明顯,這不在一個會話中,無法獲取 cookies,那麼在一些站點中,我們需要保持一個持久的會話怎麼辦呢?就像用一個瀏覽器逛淘寶一樣,在不同的選項卡之間跳轉,這樣其實就是建立了一個長久會話。 解決方案如下

1
2
3
4
5
6
import requests
s = requests.Session()
s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get("http://httpbin.org/cookies")
print(r.text)

在這裏我們請求了兩次,一次是設置 cookies,一次是獲得 cookies 運行結果

1
2
3
4
5
{
"cookies": {
"sessioncookie": "123456789"
}
}

發現可以成功獲取到 cookies 了,這就是建立一個會話到作用。體會一下。 那麼既然會話是一個全局的變量,那麼我們肯定可以用來全局的配置了。

1
2
3
4
5
6
import requests
s = requests.Session()
s.headers.update({'x-test': 'true'})
r = s.get('http://httpbin.org/headers', headers={'x-test2': 'true'})
print r.text

通過 s.headers.update 方法設置了 headers 的變量。然後我們又在請求中設置了一個 headers,那麼會出現什麼結果? 很簡單,兩個變量都傳送過去了。 運行結果

1
2
3
4
5
6
7
8
9
10
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.9.1",
"X-Test": "true",
"X-Test2": "true"
}
}

如果 get 方法傳的 headers 同樣也是 x-test 呢?

1
r = s.get('http://httpbin.org/headers', headers={'x-test': 'true'})

嗯,它會覆蓋掉全局的配置

1
2
3
4
5
6
7
8
9
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.9.1",
"X-Test": "true"
}
}

那如果不想要全局配置中的一個變量了呢?很簡單,設置為 None 即可

1
r = s.get('http://httpbin.org/headers', headers={'x-test': None})

運行結果

1
2
3
4
5
6
7
8
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.9.1"
}
}

嗯,以上就是 session 會話的基本用法

SSL 證書驗證

現在隨處可見 https 開頭的網站,Requests 可以為 HTTPS 請求驗證 SSL 證書,就像 web 瀏覽器一樣。要想檢查某個主機的 SSL 證書,你可以使用 verify 參數 現在 12306 證書不是無效的嘛,來測試一下

1
2
3
4
import requests
r = requests.get('https://kyfw.12306.cn/otn/', verify=True)
print r.text

結果

1
requests.exceptions.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:590)

果真如此 來試下 github 的

1
2
3
4
import requests
r = requests.get('https://github.com', verify=True)
print r.text

嗯,正常請求,內容我就不輸出了。 如果我們想跳過剛才 12306 的證書驗證,把 verify 設置為 False 即可

1
2
3
4
import requests
r = requests.get('https://kyfw.12306.cn/otn/', verify=False)
print r.text

發現就可以正常請求了。在默認情況下 verify 是 True,所以如果需要的話,需要手動設置下這個變量。

代理

如果需要使用代理,你可以通過為任意請求方法提供 proxies 參數來配置單個請求

1
2
3
4
5
6
7
import requests
proxies = {
"https": "http://41.118.132.69:4433"
}
r = requests.post("http://httpbin.org/post", proxies=proxies)
print r.text

也可以通過環境變量 HTTP_PROXY 和 HTTPS_PROXY 來配置代理

1
2
export HTTP_PROXY="http://10.10.1.10:3128"
export HTTPS_PROXY="http://10.10.1.10:1080"

 實戰

完整代碼

import csv
import pymysql
import time
import pandas as pd
import re
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
#連接數據庫,將csv文件保存至mysql中
def getConnect():
# 連接MySQL數據庫(注意:charset參數是utf8而不是utf-8)
conn = pymysql.connect(host='localhost',port=3307, user='root', password='liziyi123456', db='db_douban', charset='utf8')
# 創建遊標對象
cursor = conn.cursor()
# 讀取csv文件
with open('mm.csv', 'r', encoding='utf-8') as f:
read = csv.reader(f)
# 一行一行地存,除去第一行
for each in list(read)[1:]:
i = tuple(each)
# 使用SQL語句添加數據
sql = "INSERT INTO movices VALUES" + str(i) # movices是錶的名稱
cursor.execute(sql) # 執行SQL語句
conn.commit() # 提交數據
cursor.close() # 關閉遊標
conn.close() # 關閉數據庫
def getMovice(year):
server = Service('chromedriver.exe')
driver = webdriver.Chrome(service=server)
driver.implicitly_wait(60)
driver.get( "https://movie.douban.com/tag/#/?sort=S&range=0,10&tags="+year+",%E7%94%B5%E5%BD%B1")
driver.maximize_window()
actions = ActionChains(driver)
actions.scroll(0, 0, 0, 600).perform()
for i in range(50): #循環50次,爬取1000部電影
btn = driver.find_element(by=By.CLASS_NAME, value="more")
time.sleep(3)
actions.move_to_element(btn).click().perform()
actions.scroll(0, 0, 0, 600).perform()
html = driver.page_source
driver.close()
return html
def getDetails(url): #獲取每個電影的詳情
option = webdriver.ChromeOptions()
option.add_argument('headless')
driver = webdriver.Chrome(options=option)
driver.get(url=url)
html = driver.page_source
soup = BeautifulSoup(html, 'lxml')
div = soup.find('div', id='info')
#spans = div.find_all('span')
ls = div.text.split('\n')
#print(ls)
data = None;
country = None;
type = None;
time = None;
for p in ls:
if re.match('類型: .*', p):
type = p[4:].split(' / ')[0]
elif re.match('制片國家/地區: .*', p):
country = p[9:].split(' / ')[0]
elif re.match('上映日期: .*', p):
data = p[6:].split(' / ')[0]
elif re.match('片長: .*', p):
time = p[4:].split(' / ')[0]
ls.clear()
driver.quit()
name = soup.find('h1').find('span').text
score = soup.find('strong', class_='ll rating_num').text
numOfRaters = soup.find('div', class_='rating_sum').find('span').text
return {'name': name, 'data': data, 'country': country, 'type': type, 'time': time,
'score': score, 'numOfRaters': numOfRaters}
def getNameAUrl(html, year):
allM = []
soup = BeautifulSoup(html, 'lxml')
divs = soup.find_all('a', class_='item')
for div in divs:
url = div['href'] #獲取網站鏈接
i = url.find('?')
url = url[0:i]
name = div.find('img')['alt'] #獲取電影名稱
allM.append({'電影名':name,'鏈接':url})
pf = pd.DataFrame(allM,columns=['電影名','鏈接'])
pf.to_csv("movice_"+year+".csv",encoding = 'utf-8',index=False)
def getMovices(year):
allM = []
data = pd.read_csv('movice_'+year+'.csv', sep=',', header=0, names=['name', 'url'])
i = int(0)
for row in data.itertuples():
allM.append(getDetails(getattr(row, 'url')))
i += 1
if i == 2:
break
print('第'+str(i)+'部成功寫入')
pf = pd.DataFrame(allM,columns=['name','data','country','type','time','score','numOfRaters'])
pf.to_csv("mm.csv",encoding = 'utf-8',index=False,mode='a')
if __name__ == '__main__':
# 獲取電影鏈接
# htmll=getMovice('2022')
# getNameAUrl(htmll,'2022')
#獲取電影的詳細情況,保存至movice.csv中
# a=getDetails("https://movie.douban.com/subject/33459931")
# print(a)
getMovices('2022')
#getConnect()


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved