自學經驗總結+實戰:python爬蟲的自我修養與每天微信給女朋友發一份直男日報

直男日報:

# 記錄在一起多少天

# 爬取女朋友所在城市的天氣

# 每天給女朋友一句土味情話

# 爬取Bing主頁的壁紙,保存到本地併發送

 

自學爬蟲一個月左右,先用R後用python,看了許多帖子也走了不少彎路,目前可以實現R和python的靜態網頁的抓取,RSelenium的動態網頁抓取,分享一下自學的經驗。

在看別人的文章常常遇到不懂的名詞,我的建議是去把那些出現頻率高的術語弄懂,如果能加幾個python社羣就更好了。

譬如你不懂這裏的靜態網頁和動態網頁是什麼,而這兩個又對這篇文章理解有決定性作用,建議先百度之。學爬蟲的過程就像蓋一個大樓。粗略的打個比方,一開始考慮甲方的要求是什麼,根據這個要求琢磨樓怎麼蓋,畫出來模子再研究這麼蓋地暖怎麼鋪、中央空調怎麼接,接了管道之後這個大樓還穩不穩?這就要考慮水泥的質量和建材。所以爬蟲越到後面,你發現自己的焦點已經從技術變成了網頁的基礎:html協議的結構,爲什麼html源碼需要解析,js腳本的加載,CSS控制網頁佈局etc

whatever,學習最好的方式是帶着問題學,建議遇到這些問題的時候再去百度。

 

先講一下我學爬蟲的足跡,看實例的朋友可跳過這一部分

爬蟲能做什麼:在批量結構類似的網站上爬取指定信息,如豆瓣top250電影的名稱、主演、評分.....

→top250爬蟲實例,對着實例學習(個人感覺requests包和bs4包眉清目秀,比較適合上手),貼一段我的代碼,只爬了第一頁

import requests
from bs4 import BeautifulSoup

def getHTML(url):
    headers = {'user-agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'}
    response = requests.get(url, headers=headers, timeout=10)
    response.raise_for_status()
    return response.text

web = getHTML("https://movie.douban.com/top250")
soup = BeautifulSoup(web, 'html.parser')
# CSS選擇器,得到標籤
name = soup.select('.hd a > span:nth-of-type(1)')
for i in range(len(name)):
    name[i] = name[i].get_text()

→getHTML獲得網頁源碼,那response又是什麼呢?請百度:html協議的結構(注意請求頭),請求方式(注意get)。改變headers中的’user-agent'是模擬瀏覽器客戶端,如何查自己瀏覽器的user-agent自行百度。timeout=10是最大請求時間爲10s,超過這個時間腳本將不再進行嘗試訪問。response是服務器的響應,通過屬性text獲得網頁源碼,raise_for_status()函數如果訪問成功則跳過。

soup = BeautifulSoup(web, 'html.parser')

→對網頁源碼進行解析,推薦果凍公開課第四課,什麼是DOM:https://www.zhihu.com/question/34219998
不同解析器之間速度和兼容性有差別,咱剛入門也不講究這個,就用了系統自帶的html.parser。
爲什麼爬蟲還要對源碼進行解析?我的理解是,源碼是一堆字符串,解析之後可以建立不同節點之間的聯繫,讓python識別這是html文檔,從而可以利用節點之間的樹狀關係對目標數據進行定位。

# CSS選擇器,得到標籤
name = soup.select('.hd a span:nth-of-type(1)')

→如何選擇目標元素呢?這涉及選擇器的問題,一般有xpath和CSS兩種選擇器,一開始用R的時候上手的是xpath,但select()只支持CSS選擇器,這兩種選擇器在w3school都可以查到,建議學一遍。在這裏的意思是:在所有hd的class下的a標籤裏選擇第一個span標籤,可見對應的是電影中文標題。

我們查看變量name,是一個list,每個元素是對應的標籤

name

# Out
[<span class="title">肖申克的救贖</span>,
 <span class="title">霸王別姬</span>,
 <span class="title">這個殺手不太冷</span>,
 <span class="title">阿甘正傳</span>,
 <span class="title">美麗人生</span>,
 <span class="title">千與千尋</span>,
 <span class="title">泰坦尼克號</span>,
 <span class="title">辛德勒的名單</span>,
 <span class="title">盜夢空間</span>,
 <span class="title">忠犬八公的故事</span>,
 <span class="title">機器人總動員</span>,
 <span class="title">三傻大鬧寶萊塢</span>,
 <span class="title">放牛班的春天</span>,
 <span class="title">海上鋼琴師</span>,
 <span class="title">楚門的世界</span>,
 <span class="title">大話西遊之大聖娶親</span>,
 <span class="title">星際穿越</span>,
 <span class="title">龍貓</span>,
 <span class="title">熔爐</span>,
 <span class="title">教父</span>,
 <span class="title">無間道</span>,
 <span class="title">瘋狂動物城</span>,
 <span class="title">當幸福來敲門</span>,
 <span class="title">怦然心動</span>,
 <span class="title">觸不可及</span>]

然後提取每個標籤的文本信息即可,有許多不同的方式,此處使用get_text()

for i in range(len(name)):
    name[i] = name[i].get_text()

再次查看name

# Out

['肖申克的救贖',
 '霸王別姬',
 '這個殺手不太冷',
 '阿甘正傳',
 '美麗人生',
 '千與千尋',
 '泰坦尼克號',
 '辛德勒的名單',
 '盜夢空間',
 '忠犬八公的故事',
 '機器人總動員',
 '三傻大鬧寶萊塢',
 '放牛班的春天',
 '海上鋼琴師',
 '楚門的世界',
 '大話西遊之大聖娶親',
 '星際穿越',
 '龍貓',
 '熔爐',
 '教父',
 '無間道',
 '瘋狂動物城',
 '當幸福來敲門',
 '怦然心動',
 '觸不可及']

 

如果之前都好好百度的話,看到這裏恭喜你靜態網站你幾乎都可以爬了,注意在這裏我們撇開爬蟲的效率和反爬蟲不談,上面談的希望能提供一點幫助給那些想入門爬蟲的朋友。

 

下面開始我們正兒八經的實戰項目,每天給女朋友發一份直男日報

需要用到的包有

import itchat #對接微信,用python給女票發消息
import datetime #獲取當前日期並進行日期的運算
import os #操作路徑的包、
from skimage import io #從url讀取圖片,以數組格式儲存
import matplotlib #在這裏用來將數組格式的RGB圖片保存成png格式
import re #正則表達式模塊
import requests #爬蟲請求模塊
from bs4 import BeautifulSoup #解析html源碼

# 記錄在一起多少天

def get_datetime():
    begin_time = datetime.date(2018, 5, 20)
    now = datetime.date.today()
    delta = now - begin_time
    days = delta.days
    return days

日期有datetime.date和datetime.datetime兩種對象,datetime格式精確到小時分鐘秒,date年月日;兩個date對象可以做加減運算;得到timedelta對象,它的屬性days可返回int格式的天數

 

# 爬取每天的天氣

def get_HTML(url):
    '''獲取一個網頁,headers模擬瀏覽器的報頭,注意是字典形式和鍵的名稱;raise_for_status()
    如果沒有獲取成功會返回連接狀態;編碼方式用響應的apparent_encoding;在查找資源中我們只需要
    在網頁源碼中找即可,所以返回響應的文本'''
    headers = {'user-agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) \
               AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'}
    response = requests.get(url, headers=headers, timeout=10)
    response.raise_for_status()
    response.encoding = response.apparent_encoding
    return response.text

def get_weather(city):
    '''通過觀察網頁結構,得到不同天氣城市url之間的規律;將html源碼解析成DOM樹soup,不同的節點開始建立聯繫;
    利用CSS選擇器找到相應的節點提取出標籤,get('href'),get_text()得到文本,注意這兩個函數都是標籤的函數,不是
    列表'''
    url = 'http://www.weather.com.cn/textFC/' + city + '.shtml'
    web = get_HTML(url)
    soup = BeautifulSoup(web, 'html.parser')
    
# 這個網站天氣分爲白天和夜晚。存儲在不同的tag下
    weather_day = soup.select('.conMidtab3 table tr:nth-of-type(1) td:nth-of-type(3)')
    weather_day = weather_day[0].get_text()
    weather_night = soup.select('.conMidtab3 table tr:nth-of-type(1) td:nth-of-type(6)')
    weather_night = weather_night[0].get_text()
    if weather_day == weather_night:
        weather = weather_day
    else:
        weather = weather_day + '轉' + weather_night

# 同理溫度
    temperature_max = soup.select('.conMidtab3 table tr:nth-of-type(1) td:nth-of-type(5)')
    temperature_max = temperature_max[0].get_text()
    temperature_min = soup.select('.conMidtab3 table tr:nth-of-type(1) td:nth-of-type(8)')
    temperature_min = temperature_min[0].get_text()
    temperature = temperature_max + '°C' + '/' + temperature_min + '°C'

# 得到風力和風速
    wind_direction = soup.select('.conMidtab3 table tr:nth-of-type(1) td:nth-of-type(4) span:nth-of-type(1)')
    wind_direction = wind_direction[0].get_text()
    wind_force = soup.select('.conMidtab3 table tr:nth-of-type(1) td:nth-of-type(4) span:nth-of-type(2)')
    wind_force = wind_force[0].get_text()
    wind = {'wind_direction': wind_direction, 'wind_force': wind_force}
    
    w = {'weather':weather, 'temperature':temperature, 'wind':wind}
    
    return w

 

# 每天一句不同的土味情話,從網上把蒐集好的土味情話打包放到txt文件裏,爲方便代碼閱讀貼一下

def get_earthyLove(delta):
    filename = 'earthyLove.txt'
    with open(filename, 'r') as f:
        lines = []
        org_lines = f.readlines()
        for ln in org_lines:
            if len(ln) != 1:#去掉\n
                lines.append(ln)
        line = lines[delta]
    return line.split('、')[1]#去掉序號和頓號

將txt文本進行處理:去掉讀入列表裏的\n;去除序號和頓號;在這裏去除頓號的方法使用的是每一行前幾位是數字,事實上用split函數以頓號爲分隔符能達到同樣的效果。

 

#爬取每天的Bing壁紙

有很多寫相關的博客但大多對初學者不怎麼友好,要麼寫的很玄學要麼就是太講究,一開始研究了兩天正則表達式,終於渡劫爬到了壁紙的url,以爲壁紙是動態加載的大讚微軟nb,後來大佬告訴我正則表達式能找到的網頁源碼裏肯定也有,我橫着看豎着看終於發現url在<head>裏,只不過<body>裏沒有罷了...但還是建議兩種方法都掌握一下

def get_jpg(url):
    html = get_HTML(url)   
# html解析
    soup = BeautifulSoup(html, 'html.parser')
    jpg_url = soup.select('#bgLink')[0].get('href') #get()獲取標籤屬性,get_text()獲取文本
    img_url = url + jpg_url
    return img_url
# =============================================================================
# 正則表達式匹配,網上講正則的很多,建議廖雪峯老師的正則表達式和菜鳥教程對照着看,然後做一下廖雪峯老師的課後題
#     re_jpg = re.compile(r'url:.{10,90}jpg')
#     jpglist = re.findall(re_jpg, html)
#     if jpglist:
#         jpg_url = jpglist[1].split('/')[1]
#         image_url = url + jpg_url
#         return image_url
#     else:
#         print('failed')
# =============================================================================

def get_img():
    '''這裏用到skimage裏的io.imread讀取來自網頁的jpg圖片,嘗試用pyplot.imread貌似只能讀取png格式的文件'''
    url = 'https://cn.bing.com'
    img_url = get_jpg(url)
    img = io.imread(img_url)
    return img

到這裏所有的準備工作就已經完成了,剩下的就是利用itchat發消息了

def send_to_girlfriend(name, city):
    '''將所有函數打包,修改工作路徑到earthy.txt所在路徑'''
    os.chdir('C:\\Users\\mac\\Desktop\\cufe\\self\\program_learning\wxRobot')
    
# 以2018.05.20爲第一天,具體的+1和-445與代碼邏輯無關,獲取各變量
    datetime = get_datetime() + 1
    delta = datetime - 445
    earthyLove = get_earthyLove(delta)
    w = get_weather(city)
    weather = w['weather']
    tem = w['temperature']
    wind_force = w['wind']['wind_force']
    wind_direction = w['wind']['wind_direction']
    
# 搭建城市天氣url時需要拼音,而在發消息的時候需要用中文,利用字典實現
    location = {'hongkong':'香港', 'beijing':'北京', 'guangdong':'廣州', 'chongqing':'重慶'}
    city = location[city]

# 構建f-string,格式化字符串 
    msg_org = f'今天是七夕,我是XX給你準備的禮物!'
    msg_org_datetime= f'你知道嘛,我們已經在一起{datetime}天啦!'
    msg_datetime = f'我們已經在一起{datetime}天了噢!'
    if wind_direction == wind_force:#有時候天氣網站的風向和風力都一樣
        msg_weather = f'下面是rkun的天氣預報:\n今天{city}{weather}\n氣溫:{tem}\n{wind_direction}'
    else:
        msg_weather = f'下面是rkun的天氣預報:\n今天{city}{weather}\n氣溫:{tem}\n{wind_direction}:{wind_force}'
    
    msg_earthyLove = f'{earthyLove}'
    
# itchat只能發送本地圖片,無法將數組形式的圖片發送,故先用matplotlib.image.imsave儲存到本地,這個函數的
# 優點在於可以通過定義存儲路徑的後綴改變圖片格式,同時可以將數組形式的圖片保存成圖像格式
    wallpaper_index = delta + 20
    image = get_img()
    file_path = 'C:\\Users\\mac\\Desktop\\wallpaper\\th(' + str(wallpaper_index) + ').png'
    matplotlib.image.imsave(file_path, image)

# 模擬登陸網頁微信,nickName是暱稱,remarkName是備註,每次登陸網頁微信的每個好友的UserName是隨機分配的,
# itchat.send_msg()的第一個參數爲目標的Username,這既不是備註也不是暱稱,而是每次登陸隨機分配的,所以需要先用search_friends找到目標的Username
    itchat.auto_login(hotReload=True)
    whom = itchat.search_friends(nickName=name)[0]['UserName']

    if delta == 0:#下面的ifelse語句純粹是爲了讓七夕和其他日子發的消息不一樣,直接看else就好
        itchat.send_msg(msg_org, whom)
        itchat.send_msg(msg_org_datetime, whom)
        itchat.send_msg(msg_weather, whom)
        itchat.send_msg(msg_earthyLove, whom)
    else:
        itchat.send_msg(msg_datetime, whom) 
        itchat.send_msg(msg_weather, whom)
        itchat.send_msg(msg_earthyLove, whom)
        itchat.send_image(file_path, whom)

if __name__ == '__main__':#如果所有模塊成功import
    send_to_girlfriend('J', 'guangdong') 

來一張效果圖

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章