python基礎內容
## 1. 關於爬蟲的特殊性
爬蟲是一個很蛋疼的東西, 可能今天講解的案例. 明天就失效了. 所以, 不要死盯著一個網站幹. 要學會見招拆招(爬蟲的靈魂)
爬蟲程式如果編寫的不夠完善. 訪問頻率過高. 很有可能會對伺服器造成毀滅性打擊, 所以, 不要死盯著一個網站幹. 請放慢你爬取的速度. 為你好, 也為網站好.
騰訊, 阿里, 位元組, 小紅書, 美團...的網站, 反爬手段很殘忍. 新手不要去挑戰這種大廠. 等你有實力了. 慢慢研究(哪個都要研究很久....)
政府的網站, 非必要, 不爬取.非要爬取. 請一定降低訪問頻率. 為你好....
不要以為gov的網站好欺負. 那是地方性的網站. 中央的很多重要網站都有非常強力的防護措施(瑞數等...) 這種. 願意搞的. 自己研究.
爬蟲只能爬 你看得見的東西!!!!!
個人資訊不能碰!!!!!
不要妨礙人家網站伺服器的正常運營!!!!!
網站的多變性: 這個是爬蟲的魅力. 我們要全方位的去思考. 就像找漏洞一樣. 思維邏輯不可能是固定的. 達到目的即可. 不要死磕牛角尖. 要不然, 你會死的很慘.... 要學會見招拆招
關於憋程式碼這個事. 要憋. 一定要憋. 讓你憋主要有三個原因:
1. 簡單的語法使用錯誤. 不憋記不住
2. 複雜的程式邏輯. 不憋培養不出來獨立思考能力.
3. 一定要有獨立解決問題的能力.
2. 必須要掌握的py基礎
2.1 基礎語法相關
- if條件判斷
if 條件:
# 事情1
else:
# 事情2
上面就是if的最基礎的語法規則. 含義是, 如果條件為真, 去執行事情1
, 如果條件不真, 去執行事情2
. 這東西. 我就不拆開聊了. 關於if. 你要記住的事情是, 它是用來做條件判斷的
. 以後你的程式裡, 如果需要條件判斷了. 就用它....
在寫爬蟲的時候. 我們會遇到這樣的兩種情況
情況一, 資料裡有一些我們並不需要的內容
data = "10,英雄本色,1500萬" # 正常你需要的資料
data = "11,-,-" # 你不需要的資料
# 虛擬碼, 理解含義(思路)
if data裡有你不需要的資料:
再見
else:
保留
情況二, 頁面結構不統一, 會有兩種頁面結構
# 虛擬碼, 理解含義(思路)
提取器1 = xxxx # 用來提取頁面中內容的
提取器2 = xxxxxx
# 頁面有可能是不規則的。 張飛, 潘長江
結果1 = 提取器1.提取(頁面)
if 結果1:
有結果. 存起來
else:
沒有結果.
結果2 = 提取器2.提取(頁面)
2. while迴圈
關於迴圈, 我們必須要知道一個事情.
while 條件:
迴圈體
如果條件為真, 就執行迴圈體, 然後再次判斷條件.....直到條件為假. 結束迴圈.
反覆的執行一段程式碼
3. 關於True和False
True, 是真的意思. 翻譯成人話: 對的, OK, 沒毛病. 確定
False, 是假的意思. 翻譯成人話: 不對勁, 錯誤, No. 有瑕疵. 不對勁
這個應該都能看懂.
但是下面這個, 需要各位去記住
# 幾乎所有能表示為空的東西. 都可以認為是False
print(bool(0))
print(bool(""))
print(bool([]))
print(bool({}))
print(bool(set()))
print(bool(tuple()))
print(bool(None))
# 上面這一坨全是False, 相反的. 都是真. 利用這個特性. 我們可以有以下的一些寫法
# 虛擬碼, 理解邏輯.
結果 = 提取器.提取(頁面)
if 結果:
有結果. 我要儲存結果
else:
沒結果. ......
2.2 字串(萬惡之源,必須要會的,而且要相當熟悉)
字串在爬蟲
裡. 必須要知道的幾個操作:
- 索引和切片
索引, 就是第幾個字元. 它從0開始.
切片, 從字串中提取n個字元.
s = "我愛小花,小花愛我"
print(s[1])
print(s[0])
print(s[2:4]) 從第2個, 到第4個(取不到4)
- strip()
我們從網頁上提取的資料. 很多都是帶有一些雜質的(換行, 空格),怎麼去掉?
strip()可以去掉字串左右兩端
的空白(空格, 換行\n, 回車\r, 製表符\t)
s = " \t\t\t我的天哪\r\r \n\n " # 夠亂的字串
s1 = s.strip()
print(s1) # 我的天哪
- split()
split, 做切割的.
s = "10,男人本色,100000萬" # 你在網頁上提取到這樣的一段資料. 現在我需要電影名稱
tmps = s.split(",")
name = tmps[1]
print(name) # 男人本色
id, name, money = s.split(",") # 切割後. 把三個結果直接懟給三個變數
print(id)
print(name)
print(money)
- replace()
replace, 字串替換
s = "我 \t\t\n\n愛 小 花 " # 這是你從網頁上拿到的東西
s1 = replace(" ", "").replace("\t", "").replace("\n", "") # 幹掉空格, \t, \n
print(s1) # 我愛小花
- join()
join, 將列表拼接為一個完整的字串
lst = ["我媽", "不喜歡", "小花"] # 有時,由於網頁結構的不規則, 導致獲取的資料是這樣的.
s1 = "".join(lst) # 用空字串把lst中的每一項拼接起來
print(s1) # 我媽不喜歡小花
lst2 = ["\n\r","\n\r","周杰倫\n\r", "\n不認識我\r"]
s2 = "".join(lst2).replace("\n", "").replace("\r", "")
print(s2) # 周杰倫不認識我
- f-string
格式化字串的一種方案
s = "周杰倫"
s1 = f"我喜歡{s}" # 它會把一個變數塞入一個字串
print(s1) # 我喜歡周杰倫
k = 10085
s2 = f"我的電話號是{k+1}" # 它會把計算結果賽入一個字串
print(s2) # 我的電話號是10086
# 綜上, f-string的大括號裡, 其實是一段表示式.能計算出結果即可
2.3 列表
列表, 我們未來遇見的僅次於字串的一種資料型別. 它主要是能承載大量的資料. 理論上. 你的記憶體不炸. 它就能一直存
- 索引, 切片
列表的索引和切片邏輯與字串完全一致
lst = ["趙本山", "王大陸", "大嘴猴", "馬後炮"]
item1 = lst[2] # 大嘴猴
item2 = lst[1] # 王大陸
lst2 = lst[2:]
print(lst2) # ["大嘴猴", "馬後炮"]
# 注意, 如果列表中沒有資料. 取0會報錯
lst = []
print(lst[0]) # 報錯, Index out of bounds
# 注意, 如果給出的索引下標超過了列表的最大索引. 依然會報錯
lst = ["123", "456"]
print(lst[9999]) # 報錯, Index out of bounds
- 增加
給列表新增資料.
lst = [11,22]
lst.append(33)
lst.append(44)
print(lst) # [11,22,33,44]
- 刪除
刪除資料(不常用, 好不容易爬到的資料. 為什麼要刪)
lst.remove("周潤發") # 把周潤發刪掉
- 修改
lst = ["趙本山", "王大陸", "大嘴猴", "馬後炮"]
lst[1] = "周杰倫"
print(lst) # ["趙本山", "周杰倫", "大嘴猴", "馬後炮"]
- range
用for迴圈數數的一個東西
for i in range(10):
print(i) # 從0數到9
for i in range(5, 10):
print(i) # 從5 數到 9
- 查詢(必會!!!!)
lst = ["趙本山", "周杰倫", "大嘴猴", "馬後炮"]
print(lst[0])
print(lst[1])
print(lst[2])
print(lst[3])
# 迴圈列表的索引
for i in range(len(lst)):
print(lst[i])
# 迴圈列表的內容
for item in lst:
print(item)
# enumerate
lst = [11, 22, 33, 44, 55]
# 得到的是一個元組. (index, item)
for i, item in enumerate(lst):
print(i, item)
2.4 字典
字典可以成對兒的儲存資料.
- 增加
dic = {}
dic['name'] = '小魚兒'
dic['age'] = 18
print(dic) # {"name": "小魚兒", "age": 18}
- 修改
dic = {"name": "小魚兒", "age": 18}
dic['age'] = 19
print(dic) # {"name": "小魚兒", "age": 19}
- 刪除
dic = {"name": "小魚兒", "age": 18}
dic.pop("age")
print(dic) # {'name': '小魚兒'}
- 查詢(重點)
dic = {"name": "小魚兒", "age": 18}
a = dic['name'] # 查詢'name'的值
print(a) # 小魚兒
b = dic['age'] # 拿到dic中age對應的值
print(b) # 18
c = dic['哈拉少'] # 沒有哈拉少. 報錯
d = dic.get("哈拉少") # 沒有哈拉少, 不報錯. 返回None. 它好. 它不報錯
迴圈
dic = {"name": "小魚兒", "age": 18}
for k in dic: # 迴圈出所有的key
print(k)
print(dic[k]) # 獲取到所有的value並列印
巢狀
dic = {
"name": "王峰",
"age": 18,
"wife": {
"name": "章子怡",
"age": 19,
},
"children": [
{'name':"胡一菲", "age": 19},
{'name':"胡二菲", "age": 18},
{'name':"胡三菲", "age": 17},
]
}
# 王峰的第二個孩子的名字
print(dic['children'][1]['name'])
# 王峰所有孩子的名稱和年齡
for item in dic['children']:
print(item['name'])
print(item['age'])
2.5 字符集和bytes
字符集, 記住兩個字符集就夠了. 一個是utf-8, 一個是gbk. 都是支援中文的. 但是utf-8的編碼數量遠大於gbk. 我們平時使用的最多的是utf-8
# 把字串轉化成位元組
bs = "我的天哪abcdef".encode("utf-8")
print(bs) # b'\xe6\x88\x91\xe7\x9a\x84\xe5\xa4\xa9\xe5\x93\xaaabcdef'
# 一箇中文在utf-8裡是3個位元組. 一個英文是一個位元組. 所以英文字母是正常顯示的
# 把位元組還原回字串
bs = b'\xe6\x88\x91\xe7\x9a\x84\xe5\xa4\xa9\xe5\x93\xaaabcdef'
s = bs.decode("utf-8")
print(s)
記住, bytes不是給人看的. 是給機器看的. 我們遇到的所有文字, 圖片, 音訊, 影片. 所有所有的東西到了計算機裡都是位元組.
2.6 檔案操作
python中. 想要處理一個檔案. 必須用open()先開啟一個檔案
語法規則:
f = open(檔名, mode="模式", encoding='檔案編碼')
f.read()|f.write()
f.close()
模式:
我們需要知道的主要有4個. 分別是: r, w, a, b
- r 只讀模式. 含義是, 當前這一次open的目的是讀取資料. 所以, 只能讀. 不能寫
- w 只寫模式. 含義是, 當前這一次open的目的是寫入資料. 所以, 只能寫, 不能讀
- a 追加模式. 含義是, 當前這一次open的目的是向後追加. 所以, 只能寫, 不能讀
- b 位元組模式. 可以和上面三種模式進行混合搭配. 目的是. 寫入的內容或讀取的內容是位元組.
問:
- 如果我想儲存一張圖片. 應該用哪種模式?
- 我想讀取txt檔案, 用哪種模式?
- 我想複製一個檔案. 應該用哪種模式?
encoding: 檔案編碼. 只有處理的檔案是文字的時候才能使用. 並且mode不可以是b
. 99%的時候我們用的是utf-8
另一種寫法:
with open(檔名, mode=模式, encoding=編碼) as f:
pass
這種寫法的好處是, 不需要我們手動去關閉f
讀取一個文字檔案:
with open("躺屍一擺手.txt", mode="r", encoding="utf-8") as f:
for line in f: # for迴圈可以逐行的進行迴圈檔案中的內容
print(line)
2.7 關於函式
在程式碼量很少的時候, 我們並不需要函式. 但是一旦程式碼量大了. 一次寫個幾百行程式碼. 除錯起來就很困難. 此時, 建議把程式改寫成一個一個具有特定功能的函式. 方便除錯. 也方便程式碼的重用
def 函式名(形式引數):
# 函式體
return 返回值
上面是編寫一個函式的固定邏輯. 但是, 編寫好的函式是不會自己執行的. 必須有人呼叫才可以
函式名(實際引數)
寫一個試試:
def get_page_source(url):
print("我要傳送請求了. 我要獲取到頁面原始碼啊")
return "頁面原始碼"
pg_one = get_page_source("baidu.com")
pg_two = get_page_source("koukou.com")
再來一個
def download_image(url, save_path):
print(f"我要下載圖片{url}了", f"儲存在{save_path}")
donwload_image("http://www.baidu.com/abc/huyifei.jpg", "胡二飛.jpg")
donwload_image("http://www.baidu.com/aaa/dagedagefeifeifei.jpg", "大哥大哥飛飛飛.jpg")
總結, 函式的好處就是, 以後需要該功能. 不用再寫重複程式碼了.
2.8 關於模組
模組是啥? 模組就是已經有人幫我們寫好了的一些程式碼, 這些程式碼被儲存在一個py檔案或者一個資料夾裡. 我們可以拿來直接用
在python中有三種模組.
第一種, python內建模組
不用安裝. 直接匯入就能用
第二種, 第三方模組
需要安裝. 安裝後. 匯入就可以用了
第三種, 自定義模組(新手先別自己定義模組)
直接匯入就能用
匯入模組的語法
import 模組
from 模組 import 功能
from 模組.子模組 import 功能
舉例子,
import os
import sys
from urllib.parse import urljoin
from bs4 import BeautifulSoup
搞爬蟲.必須要了解的一些python內建模組:
(1) time模組
import time
time.time() # 這個是獲取到時間戳
time.sleep(999) # 讓程式暫停999秒
(2) OS模組
import os
# 判斷檔案是否存在
os.path.exists() # 判斷檔案或者資料夾是否存在
os.path.join() # 路徑拼接
os.makedirs() # 建立資料夾
(3) json模組(重中之重)
現在的網站不同於從前了. 習慣性用json來傳遞資料. 所以, 我們必須要知道json是啥, 以及python如何處理json.
json是一種類似字典一樣的東西. 對於python而言, json是字串.
s = '{"name": "jay", "age": 18}'
如何來轉化它:
json字串 => python字典
import json
s = '{"name": "jay", "age": 18}'
dic = json.loads(s)
print(type(dic))
python字典 => json字串
import json
dic = {"name": "jay", "age": 18}
s = json.dumps(dic)
print(type(s))
(4) random模組
隨機. 沒別的用處.生成隨機數
import random
i = random.randint(1, 10) # 1~10的隨機數
print(i) # 多跑兩次.效果更加
(5) 異常處理
這個是重點. 我們在寫爬蟲的時候. 非常容易遇到問題. 但這些問題本身並不是我們程式的問題.
比如, 你在抓取某網站的時候. 由於網路波動或者他伺服器本身壓力太大. 導致本次請求失敗. 這種現象太常見了. 此時, 我們程式這邊就會崩潰. 列印一堆紅色的文字. 讓你難受的一P. 怎麼辦?
我們要清楚一個事情. 我們平時在開啟一個網址的時候. 如果長時間沒有反應, 或者載入很慢的時候. 我們習慣性的會重新整理網頁. 對吧. 這個邏輯就像: 程式如果本次請求失敗了. 能不能重新來一次
. OK, 我們接下來聊的這個異常處理. 就是幹這個事兒的.
try: # 嘗試...
print("假如, 我是一段爬蟲程式碼, 請求到對方伺服器")
print("我得出事兒啊")
print(1/0) # 出事兒了
except Exception as e: # 出錯了. 我給你兜著
print(e) # 怎麼兜? 列印一下. 就過去了
print("不論上面是否出錯. 我這裡, 依然可以執行")
看懂了麼? 程式執行的時候. 如果try
中的程式碼出現錯誤. 則自動跳到except
中. 並執行except
中的程式碼. 然後程式正常的, 繼續執行
有了這玩意. 我們就可以寫出一段很漂亮的程式碼邏輯:
while 1:
try:
我要傳送請求了. 我要幹美國CIA的總部. 我要上天
print("我成功了!!")
break # 成功了.就跳出迴圈
except Exception as e:
print("失敗了")
print("我不怕失敗")
print("再來")
改良版:
import time
for i in range(10):
try:
我要傳送請求了. 我要幹美國CIA的總部. 我要上天
print("我成功了!!")
break # 成功了.就跳出迴圈
except Exception as e:
print("失敗了")
print("我不怕失敗")
print("再來")
time.sleep(i * 10)