網(wǎng)上的教程,基本上都很簡單,直接上了一大段代碼,和大家說程序OK了
但我初學(xué)爬蟲,還是什么都看不懂
所以,這是一個特別啰嗦的教程
完整代碼
Python爬蟲:獲取扇貝打卡信息
Python爬蟲:將爬蟲結(jié)果保存到Excel
Python爬蟲:從TXT導(dǎo)入數(shù)據(jù)
(如果你懶得看教程,可以直接看代碼)
什么是爬蟲?
爬蟲是自動化幫我們獲取網(wǎng)頁數(shù)據(jù)的程序
簡單來說,用爬蟲,去獲取網(wǎng)頁數(shù)據(jù)
那么,我們大概需要這么幾個步驟
- 明確目標(biāo):首先確定,我們要爬蟲什么內(nèi)容
- 打開網(wǎng)頁:確定網(wǎng)頁網(wǎng)址,得到網(wǎng)頁響應(yīng)
- 定位數(shù)據(jù):找到我們需要的數(shù)據(jù)
- 清洗數(shù)據(jù):把我們要的那部分?jǐn)?shù)據(jù)截出來
- 保存數(shù)據(jù):把數(shù)據(jù)保存到EXCEL或者txt
明確目標(biāo)
我們以扇貝網(wǎng)為例,講解爬蟲
簡單介紹一下,扇貝網(wǎng)是一個學(xué)英語的網(wǎng)站,里面有個小組,小組里的成員一起打卡
我爬蟲的目標(biāo)是:得到小組成員一周的打卡時間匯總,以及對應(yīng)的學(xué)習(xí)內(nèi)容
??
下面開始爬蟲,出發(fā)!
打開網(wǎng)頁
首先打卡網(wǎng)頁,各個網(wǎng)頁的網(wǎng)址不一樣,大家要嘗試去發(fā)現(xiàn)規(guī)律
比如扇貝網(wǎng)每一位同學(xué),會有一個ID,我的ID是16888030
那我先定義我的ID,用input把我的ID輸進去
ID = input("請輸入你的扇貝ID:")
web代表網(wǎng)頁地址,str將我的ID從數(shù)字轉(zhuǎn)換成字符串。下面兩種表達方式完全等效。
web = "https://www.shanbay.com/api/v1/checkin/user/"+str(ID)+"/"
web = "https://www.shanbay.com/api/v1/checkin/user/16888030/"

然后開始獲取網(wǎng)頁
from urllib.request import urlopen # 加入urllib模塊
web = "https://www.shanbay.com/api/v1/checkin/user/"+str(ID)+"/"
shanbay = urlopen(web) # 打開網(wǎng)址
urllib 是Python 中用于獲取網(wǎng)頁數(shù)據(jù)的模塊,通過import調(diào)用它。我們用urlopen打開網(wǎng)址,這時候print(shanbay),輸出shanbay的內(nèi)容
?
我們可以看到“Response”,代表我們成功獲得了對方網(wǎng)址給我們的回答
那回答的內(nèi)容是什么呢?通過read打開網(wǎng)址
shanbay_data = shanbay.read().decode()
這時候輸出shanbay_data,可以看到網(wǎng)頁內(nèi)容已經(jīng)被讀取

定位數(shù)據(jù)
網(wǎng)頁已經(jīng)讀取成功,接下來我們就需要定位我們需要的數(shù)據(jù)

定位內(nèi)容:
在剛剛獲取的網(wǎng)頁內(nèi)容中,看到了我們需要的內(nèi)容
-
bdc代表學(xué)習(xí)單詞情況 -
listen代表學(xué)習(xí)聽力情況 -
sentence代表學(xué)習(xí)句子情況
定位開始和結(jié)束位置:
我們發(fā)現(xiàn),這些數(shù)據(jù)最開始的部分,是從“stats”開始的,到“track_object_img”結(jié)束
- 這里注意,選擇離你要的內(nèi)容近一點的定位目標(biāo)
- 定位近,為后續(xù)字符串操作提供便利
- 定位太遠(yuǎn)了,就需要刪減很多內(nèi)容
那我們首先,先把從“stats”到“track_object_img”的內(nèi)容提出來
find_data = re.findall("\"stats\".*?track_object_img" ,shanbay_data)
這里我們調(diào)用了re 模塊來使用正則表達式
正則表達式:使用單個字符串來描述、匹配一系列符合某個句法規(guī)則的字符串
官方文檔:re — Regular expression operations
怎么定位到我們需要的內(nèi)容?用search或者find來找
re.search()
re.findall()
具體內(nèi)容怎么表達?用符號匹配
^ # 匹配字符串的開頭
$ # 匹配字符串結(jié)尾
. # 匹配任意字符
* # 表示任意次(從0到無限)
+ # 表示至少一次或任意次數(shù)
[0-9] # 從0至9共十個數(shù)字中的任意一個
[a-z] # 從小寫a到z,26個字母中的一個
[A-Z] # 從大寫A到Z,26個字母的一個
\s # 用于匹配單個空格,包括tab鍵和換行符
\S # 用于匹配單個空格之外的所有字符
\d # 匹配0-9的數(shù)字
\w # 匹配字母、數(shù)字或下劃線
舉例:我們前面說過,要把“stats”到“track_object_img”的內(nèi)容提出來,我們就可以這么表達
re.findall("\"stats\".*?track_object_img" ,shanbay_data)
講解一下這句代碼:
1、 用findall找出所有從“stats”到“track_object_img”的內(nèi)容
2、\"stats\"(開始位置).(匹配任意字符)*(任意次數(shù))track_object_img(結(jié)束位置)
此處可能會有兩個疑問:
-
\"stats\"為什么要\",不直接"?
因為"在Python中有特殊作用("字符串","是字符串的標(biāo)志),所以我們用到"的時候,用\"來表示 - 代碼中間那個
?要來干嘛的?
是避免貪婪匹配。
貪婪匹配:我從A匹配到B,默認(rèn)是從第一個A匹配到最后一個B
非貪婪匹配:第一個A匹配到第一個B
到這一步,我們就把所有我們需要的內(nèi)容提取出來了,下面開始具體清洗提取我們要的數(shù)據(jù)。
清洗數(shù)據(jù)
find_data = re.findall("\"stats\".*?track_object_img" ,shanbay_data)
經(jīng)過上一步,我們把所有需要的內(nèi)容都保存在find_data中,find_data里面此時有20條數(shù)據(jù),是我最近20天的打卡情況

我們仔細(xì)觀察find_data數(shù)據(jù),可以看到,打卡的內(nèi)容是有規(guī)律的。
"stats": {"sentence": {"num_today": 5, "used_time": 2.0}, "bdc": {"num_today": 26, "used_time": 5.0}, "listen": {"num_today": 11, "used_time": 14.0}}, "track_object_img"
所有的內(nèi)容,都會有一個num_today和used_time。就是你今天學(xué)了多少,花了多少時間。這兩個數(shù)據(jù),就是我們想要的。開心,繼續(xù)往下~
我需要一條一條把信息讀出來,這就需要一個for循環(huán)
for data in find_data:
read = re.findall("\"read\":.*?}",data)
if read == []:
read = "{num_today\": 0, \"used_time\": 0.0}"
listen = re.findall("\"listen\":.*?}",data)
if listen == []:
listen = "{num_today\": 0, \"used_time\": 0.0}"
sentence = re.findall("\"sentence\":.*?}",data)
if sentence == []:
sentence = "{num_today\": 0, \"used_time\": 0.0}"
bdc = re.findall("\"bdc\":.*?}",data)
if bdc == []:
bdc = "{num_today\": 0, \"used_time\": 0.0}"
用for循環(huán)依次取讀20條數(shù)據(jù),提取里面的閱讀read、聽力listen、句子sentence、單詞bdc數(shù)據(jù)
這里我們用一個if判斷,如果find_data里面有這部分內(nèi)容,就提取出來,如果沒有(也就是我們沒有學(xué)習(xí)這部分),我們就構(gòu)造一段字符串{num_today\": 0, \"used_time\": 0.0},把num_today和used_time設(shè)置為0,和其他內(nèi)容統(tǒng)一起來
大家可以看到,我用了4個re.findall,把閱讀read、聽力listen、句子sentence、單詞bdc數(shù)據(jù)取出來
以單詞bdc為例,這時候提取的數(shù)據(jù)如下

怎么具體提取出60和11.0這兩個數(shù)字呢?
bdc_num = re.findall(r"\d+\.?\d*",str(bdc))[0]
bdc_time = re.findall(r"\d+\.?\d*",str(bdc))[1]
還是用re正則化表達,用\d把所有數(shù)字提取出來,第一個數(shù)字賦值給bdc_num,第二個數(shù)字賦值給bdc_time

操作到這一步,所有的信息相當(dāng)于都提取好了
下面我們來思考一個問題,統(tǒng)計時間怎么確定?
我想要統(tǒng)計最近一周的打卡情況
設(shè)置時間
import datetime # 先把datetime這個模塊導(dǎo)入進來
(1)怎么確定查卡時間?可以有兩種操作方式
now = datetime.datetime.now() # 輸入查卡日期,默認(rèn)是今天
now = datetime.date(2019,5,13) # 輸入查卡日期,自定義
(2) 怎么定義范圍?我只想統(tǒng)計一周的數(shù)據(jù)
time2 = datetime.timedelta(days=8) # 統(tǒng)計一個星期的數(shù)據(jù)
day_now = str(now).split(" ")[0] # 把日期取出來,后面的幾點幾分就不要了
day_end = now - time2 # 計算統(tǒng)計結(jié)束的那天
(3) 獲取網(wǎng)頁中的打卡時間
# 獲取打卡天數(shù)
checkin_time = []
num_checkin_days = []
find_checkin = re.findall("\"checkin_time\".*?\"share_urls\"",shanbay_data)
for checkin in find_checkin:
shanbey_time = checkin.split(",")[0]
shanbey_days = checkin.split(",")[3]
checkin_time.append(str(shanbey_time)[len("\"checkin_time\": \""):len("\"checkin_time\": \"")+10])
num_checkin_days.append(str(shanbey_days)[len("\"num_checkin_days\": "):])
這里我們引入字符串的基本操作
str() 將數(shù)值轉(zhuǎn)變?yōu)樽址?str[::1] 將字符串倒序
ord() 獲取字符的整數(shù)表示
chr() 把編碼轉(zhuǎn)換為對應(yīng)的字符
.join() 串聯(lián)若干字符
.format() 可以將字符串中的部分字符變成變量
.upper() 將字符串中的所有英文字母變成大寫
.lower() 將字符串中的所有英文字母變成小寫
.swapcase() 將字符串中的所有英文字母大小寫互換
.title() 所有單詞首字母大寫
.split() 切割字符串
.strip() 刪去字符串開頭和結(jié)尾的空格或字符
.lsrtip() 與.strip()功能相似,從字符串左側(cè)開始,遇到第一個不需要移除的字符則停止
.replace() 替換字符串中的某一部分
b.find(a) 返回字符串 a 在字符串 b中第一次出現(xiàn)所在的索引位置
我們先來看,用find_all把打卡時間這部分的數(shù)據(jù)提取出來,同樣是20條數(shù)據(jù),包含了打卡的具體時間

打開其中一條數(shù)據(jù),發(fā)現(xiàn)我們要的時間
2019-05-22和打卡天數(shù)538在不同的位置
這里我們用.split(),先根據(jù)逗號,把這個字符串分開
shanbey_time = checkin.split(",")[0]
shanbey_days = checkin.split(",")[3]

然后截取字符串,用
len()統(tǒng)計前面那些字符的個數(shù),這些字符就不要了用
append依次把內(nèi)容加上去
checkin_time.append(str(shanbey_time)[len("\"checkin_time\": \""):len("\"checkin_time\": \"")+10])
num_checkin_days.append(str(shanbey_days)[len("\"num_checkin_days\": "):])


這時候,我們只要再上一個
if 判斷一下,統(tǒng)計從昨天開始的打卡記錄
if checkin_time[count] >= day_now:
count += 1
elif checkin_time[count] > day_end:
# 統(tǒng)計總時間和各項時間
time_total = float(read_time)+float(listen_time)+float(bdc_time)+float(sentence_time);
time_read = time_read+float(read_time);
time_listen = time_listen+float(listen_time);
time_bdc = time_bdc+float(bdc_time);
time_sentence = time_sentence+float(sentence_time);
# 統(tǒng)計各項數(shù)目累計
count_read = count_read+float(read_num)
count_listen = count_listen+float(listen_num)
count_bdc = count_bdc+float(bdc_num)
count_sentence = count_sentence+float(sentence_num)
# 輸出一周每日打卡情況
print("{},打卡{}天:閱讀{}篇,聽力{}句,單詞{}個,煉句{}句,學(xué)習(xí)時間{}分鐘".format(checkin_time[count],num_checkin_days[count],read_num,listen_num,bdc_num,sentence_num,time_total))
count += 1
else:
break
最后輸出結(jié)果
print("單詞:{}分鐘,總計{}個".format(time_bdc,count_bdc))print("閱讀:{}分鐘,總計{}篇".format(time_read,count_read))
print("煉句:{}分鐘,總計{}句".format(time_sentence,count_sentence))
print("聽力:{}分鐘,總計{}句".format(time_listen,count_listen))
print('\n')
print("打卡時長:{}分鐘".format(time_read+time_sentence+time_bdc+time_listen))
保存數(shù)據(jù)
在獲取自己的打卡情況之后,我覺得這種都輸在屏幕上的內(nèi)容,很難整理,不適合小組打卡。我需要它能自動保存到Excel
這里要注意,用到Excel相關(guān)功能的時候,要導(dǎo)入相關(guān)庫
pip install xlwt
import xlwt # 把Excel輸出模塊加進來
# 定義保存Excel的位置
workbook = xlwt.Workbook() #定義workbook
sheet = workbook.add_sheet('本周打卡') #添加sheet
head = ['打卡', '單詞', '閱讀', '煉句', '聽力', '學(xué)習(xí)時間'] #表頭
for h in range(len(head)):
sheet.write(0, h, head[h]) #把表頭寫到Excel里面去
# 把內(nèi)容保存到Excel
sheet.write(i, 0, checkin_time[count]) # 第i行,第1列
sheet.write(i, 1, bdc_num) # 第i行,第2列
sheet.write(i, 2, read_num) # 第i行,第3列
sheet.write(i, 3, sentence_num) # 第i行,第4列
sheet.write(i, 4, listen_num) # 第i行,第5列
sheet.write(i, 5, time_total) # 第i行,第6列
# 保存Excel表
workbook.save('C:/Users/Administrator/Desktop/扇貝打卡.xls')
print('寫入excel成功')
print("文件位置:")
print("C:/Users/Administrator/Desktop/扇貝打卡.xls")
此時程序運行效果如下:
接著,小組有這么多ID,每次改一改,我都要手動輸,那太麻煩了。
我需要一個代碼,把ID自動導(dǎo)入程序
#從txt導(dǎo)入數(shù)據(jù)
ID_total_input = open('C:/Users/Administrator/Desktop/user.txt')
ID_total = ID_total_input.read()
ID_total = ID_total.split("\n") # 如果輸入多個ID,用“\n”分開
自動讀取ID、查卡、保存到EXCEL
最后,思考一下,需要導(dǎo)出小組打卡的哪些數(shù)據(jù)內(nèi)容,調(diào)整代碼
小組打卡輸出EXCEL情況如下:(昵稱和ID做了打碼處理)
完整代碼
Python爬蟲:獲取扇貝打卡信息
Python爬蟲:將爬蟲結(jié)果保存到Excel
Python爬蟲:從TXT導(dǎo)入數(shù)據(jù)
Python遠(yuǎn)不止爬蟲
坦白說,在學(xué)習(xí)編程40天的時候,我能寫出小組查卡代碼,我是非常欣喜和嘚瑟的。我還去小組技術(shù)群和扇貝編程群,要求大家表揚我,哈哈。
爬蟲很有用。我日常是科研狗,整理課題相關(guān)的6000多篇文獻,去年我用了半個月,今年我用了2個小時寫了個代碼。
但是Python的內(nèi)容遠(yuǎn)不止爬蟲。
我們小組的組員,因為工作需要,需要將幾百頁PDF文檔中的內(nèi)容轉(zhuǎn)成EXCEL表格,Python幾十行代碼搞定
import pdfplumber
import xlwt
# 定義保存Excel的位置
workbook = xlwt.Workbook() #定義workbook
sheet = workbook.add_sheet('Sheet1') #添加sheet
i = 0 # Excel起始位置
path = input("請輸入PDF文件位置:")
#path = "aaaaaa.PDF" # 導(dǎo)入PDF路徑
pdf = pdfplumber.open(path)
print('\n')
print('開始讀取數(shù)據(jù)')
print('\n')
for page in pdf.pages:
# 獲取當(dāng)前頁面的全部文本信息,包括表格中的文字
# print(page.extract_text())
for table in page.extract_tables():
# print(table)
for row in table:
print(row)
for j in range(len(row)):
sheet.write(i, j, row[j])
i += 1
print('---------- 分割線 ----------')
pdf.close()
# 保存Excel表
workbook.save('C:/Users/Administrator/Desktop/PDFresult.xls')
print('\n')
print('寫入excel成功')
print('保存位置:')
print('C:/Users/Administrator/Desktop/PDFresult.xls')
print('\n')
input('PDF取讀完畢,按任意鍵退出')
另外,做PPT圖表總是很丑?
??
Python這么多好看的圖表,只要改改參數(shù),你就能擁有。不考慮一下?
數(shù)據(jù)可視化:pygal
Python其他我未知的功能,等我學(xué)習(xí)了再來和大家分享。
最后
以上內(nèi)容都只是入門代碼,爬蟲代碼中也不涉及編寫函數(shù)、賬號密碼登入等內(nèi)容。我的編程課程還沒有結(jié)束,學(xué)習(xí)永無止境。
為什么要寫下這個帖子,來和大家一起分享代碼?
因為我們組員Grit說過:
Learning by doing. Learning by teaching.
和大家共勉,一起學(xué)習(xí)