python 爬取喜馬拉雅節目生成RSS Feed

swayer發表於2020-12-03

記錄於:2020年12月03日
用了N年的手機在經歷N次掉落之後終於扛不住了,後背都張嘴了,估計再摔一次電池都能飛出來。
換了手機,由於之前有聽喜馬拉雅的習慣,但是手機裡自帶有播客軟體,強迫症逼著我不能下載喜馬拉雅app。
找了幾天沒發現喜馬拉雅提供的有RSS訂閱(後來想了一下,別人怎麼可能提供這個功能,O(∩_∩)O哈哈~),網上也沒有相關服務。
苦啊,後來還是下載了喜馬拉雅app,但是實在受不了,就索性自己搗鼓一個輪子。

訴求很簡單,就是想將喜馬拉雅的節目搬到播客軟體,用原生的app聽第三方的資料,這個需求好惡心啊,還好不是產品經理提的。

好吧,開始吧。
其實寫爬蟲,重要的不是程式碼實現,而是剛開始對需要爬取的資料的分析,分析怎麼爬取,怎麼得到自己的資料,只要這個流程明白了。程式碼實現就很簡單了。

在這之前需要知道什麼是RSS

分析

瀏覽器開啟喜馬拉雅,找到想聽的節目,比如:郭德綱

 

 

 這樣就有了爬取專案啦,對著這個頁面開始分析,我需要標題,作者,圖片三個元素,開啟瀏覽器F12,找到這三個元素的定位,這樣只需要相應的程式碼就能抓取資訊了,這些資訊就足夠生成RSS中的<channel> 元素啦。

重要的是<item> 元素,播客播的就是這個元素中的資訊。
其實就是要拿到頁面上的 [播放列表],還是F12找到 [播放列表]的定位,有了定位,就可以抓取出這個列表,並獲取這個列表中每個元素的連結,通過此連結就可以進去詳情頁。

 

點開詳情頁,離實現越來越近了。
我需要標題,描述,及播放源這三個元素來構成<item> 元素。
標題和描述很好獲取,還是老套路F12定位就可以了,播放源就需要觀察了,開啟F12,觀察詳情頁有哪些請求,看是否有某些請求得到聲音源資料,
通過發現:https://www.ximalaya.com/revision/play/v1/audio 這個請求,會響應資料播放資料

這就能拿到播放資料啦。這樣一來,第一頁的所有播放資料都能拿到了。

由於當前是列表頁,所以少不了分頁,我們只需要找出當前頁面是否存在下一頁,且找到下一頁的連結,發起請求然後重複步驟,這樣就能拿到整個列表頁。    

                               

 有了上面的一通分析,就知道了如何去編寫程式碼實現這個功能啦。

編碼

按照上面的流程,進行編碼

 1.構建Channel物件

 2.構建Item物件

 3.生成RSS(在同級目錄下會生成一個xml檔案)

import requests
from bs4 import BeautifulSoup
import datetime

##################################
#####   公用物件,儲存/生成    ######
##################################

# rss channel
class channel(object):

    def __init__(self, title, author, image):
        self.title = title
        self.author = author
        self.image = image

# rss item
class item(object):

    def __init__(self, title, pubDate, description,enclosure):
        self.title = title
        self.pubDate = pubDate
        self.description = description
        self.enclosure = enclosure


##################################
#####     爬取資料,儲存      ######
##################################

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36'
    }


# 開始頁 - 郭德綱21年相聲精選
mainUrl = "https://www.ximalaya.com/xiangsheng/9723091/"
# 播放地址
playV1 = "/revision/play/v1/audio?id={}&ptype=1"
# gmt時間格式化
GMT_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'
# 網址
ximalaya = mainUrl[:mainUrl.index('/',8)]
# 所有播放項
items = []


# 構建Channel物件
def getChannel():
    r = requests.get(mainUrl, headers=headers)
    soup = BeautifulSoup(r.text, 'html.parser')
    title = soup.find('h1', attrs={'class': 'title vA_'}).text
    author = soup.find('a',attrs={'class':'nick-name gK_'}).text
    image = "http:" + soup.find('img', attrs={'class': 'img vA_'})['src'].split('!')[0]
    return channel(title, author, image)


# 構建Item物件
def getItem(listPageUrl):

    print('======> 正在爬取列表頁',listPageUrl)
    r = requests.get(listPageUrl, headers=headers)
    soup = BeautifulSoup(r.text, 'html.parser')

    # 獲取所有播放列表項詳情
    soundList = soup.find_all('div', attrs={'class': 'text lF_'})
    for sound in soundList:
        getDetails(ximalaya + sound.a['href'])

    # 進入下一頁
    pageNext = soup.find('li', attrs={'class': 'page-next page-item WJ_'})
    if pageNext:
        getItem(ximalaya + pageNext.a['href'])


# 進入詳情頁
def getDetails(detailPageUrl):

    print("======> 正在爬取詳情頁",detailPageUrl)

    r = requests.get(detailPageUrl, headers=headers)
    soup = BeautifulSoup(r.text, 'html.parser')

    # 標題
    title = soup.find('h1', attrs={'class': 'title-wrapper _uv'}).text
    # 釋出時間
    pubDate = soup.find('span', attrs={'class': 'time _uv'}).text
    # 聲音簡介
    description = ""
    if soup.find('article'):
        description = soup.find('article').text

    # 播放源
    playUrl = ximalaya + playV1.format(detailPageUrl.split('/')[-1]);
    r = requests.get(playUrl, headers=headers)
    enclosure = r.json()['data']['src']

    items.append( item(title,datetime.datetime.strptime(pubDate, '%Y-%m-%d %H:%M:%S').strftime(GMT_FORMAT),description,enclosure) )


##################################
#####        生成RSS        ######
##################################

def createRSS(channel):

    rss_text = r'<rss ' \
               r' xmlns:atom="http://www.w3.org/2005/Atom" ' \
               r' xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" ' \
               r' version="2.0" ' \
               r' encoding="UTF-8"> ' \
               r' <channel>' \
               r' <title>{}</title>' \
               r' <itunes:author>{}</itunes:author>' \
               r' <itunes:image href="{}"/>' \
        .format(channel.title, channel.author, channel.image)

    for item in items:
        rss_text += r' <item>' \
                    r'  <title>{}</title>' \
                    r'  <description><![CDATA[{}]]></description>' \
                    r'  <enclosure url="{}" type="audio/mpeg"/>' \
                    r' </item>'\
            .format(item.title, item.description, item.enclosure)

    rss_text += r' </channel></rss>'

    print('======> 生成RSS')
    print(rss_text)

    #寫入檔案
    with open(mainUrl.split('/')[-2]+'.xml', 'w' ,encoding='utf-8') as f:
        f.write(rss_text)


if __name__=="__main__":

    channel = getChannel()
    getItem(mainUrl)
    createRSS(channel)

python 爬取喜馬拉雅節目生成RSS Feed

將生成後的xml放到伺服器,就可以盡情享用了。

成果

python 爬取喜馬拉雅節目生成RSS Feedpython 爬取喜馬拉雅節目生成RSS Feedpython 爬取喜馬拉雅節目生成RSS Feedpython 爬取喜馬拉雅節目生成RSS Feedpython 爬取喜馬拉雅節目生成RSS Feed

 易中天老師講的真的好

後續

 本文編寫於2020年12月3日,後續官方可能會對頁面進行更改,請求進行更改等,會導致以上爬蟲失效,所以需要知道如何進行分析,才能知道如何爬取。

 以上程式碼只作為學習探討,請問惡意使用!

相關文章