要求:在B站搜索多個(gè)關(guān)鍵字,并將搜索到的視頻結(jié)果(包括視頻名流强,視頻連接,點(diǎn)擊量呻待,收藏量打月,彈幕數(shù)和長(zhǎng)傳時(shí)間)錄入數(shù)據(jù)庫(kù)
了解網(wǎng)頁(yè)
爬取數(shù)據(jù)前,我們需要先了解網(wǎng)頁(yè)結(jié)構(gòu)带污,以B站首頁(yè)為例https://www.bilibili.com/
僵控,按快捷鍵【Ctrl+U】打開(kāi)源碼頁(yè)面
認(rèn)識(shí)網(wǎng)頁(yè)結(jié)構(gòu)
網(wǎng)頁(yè)一般由三部分組成,分別是 HTML(超文本標(biāo)記語(yǔ)言)鱼冀、CSS(層疊樣式表)和 JScript(活動(dòng)腳本語(yǔ)言)报破。
HTML
HTML 是整個(gè)網(wǎng)頁(yè)的結(jié)構(gòu),相當(dāng)于整個(gè)網(wǎng)站的框架千绪。帶“<”充易、“>”符號(hào)的都是屬于 HTML 的標(biāo)簽,并且標(biāo)簽都是成對(duì)出現(xiàn)的荸型。
標(biāo)簽 | |
---|---|
<head>..</head> | 包含了所有的頭部標(biāo)簽元素盹靴。在 <head>元素中可以插入腳本(scripts), 樣式文件(CSS),及各種 meta 信息瑞妇「寰玻可以添加的元素標(biāo)簽為: <title>, <style>, <meta>, <link>, <script>, <noscript> 和 <base>。 |
<html>..</html> | 表示標(biāo)記中間的元素是網(wǎng)頁(yè) |
<body>..</body> | 表示用戶(hù)可見(jiàn)的內(nèi)容 |
<div>..</div> | 表示框架 |
<p>..</p> | 表示段落 |
<li>..</li> | 表示列表 |
<img>..</img> | 表示圖片 |
<h1>..</h1> | 表示標(biāo)題 |
<a href="">..</a> | 表示超鏈接 |
CSS
CSS 表示樣式辕狰,表示下面引用一個(gè) CSS改备,在 CSS 中定義了外觀(guān)。
JScript
JScript 表示功能蔓倍。交互的內(nèi)容和各種特效都在 JScript 中悬钳,JScript 描述了網(wǎng)站中的各種功能盐捷。
如果用人體來(lái)比喻,HTML 是人的骨架默勾,并且定義了人的嘴巴碉渡、眼睛、耳朵等要長(zhǎng)在哪里母剥。CSS 是人的外觀(guān)細(xì)節(jié)滞诺,如嘴巴長(zhǎng)什么樣子,眼睛是雙眼皮還是單眼皮环疼,是大眼睛還是小眼睛铭段,皮膚是黑色的還是白色的等。JScript 表示人的技能秦爆,例如跳舞、唱歌或者演奏樂(lè)器等憔披。
寫(xiě)一個(gè)簡(jiǎn)單的 HTML
打開(kāi)一個(gè)記事本等限,輸入下面的內(nèi)容:
輸入代碼后,保存記事本芬膝,然后修改文件名和后綴名為"HTML.html"望门,運(yùn)行該文件后的效果,如圖锰霜。<head>
<title> 這里是標(biāo)題</title>
</head>
<body>
<div style="color:#0000FF">
<h1>這是被第一個(gè)div定義的標(biāo)題1</h1>
<p>這里是被第一個(gè)div定義第一個(gè)段落</p>
</div>
<div style="color:#00FF00">
<ul>
<li><a >列表內(nèi)容1 百度鏈接</a></li>
<li>列表內(nèi)容2</li>
</ul>
</div>
</body>
使用 requests 庫(kù)請(qǐng)求網(wǎng)站
若未安裝 requests 庫(kù)筹误,可在終端中使用pip安裝庫(kù):
pip install requests
爬蟲(chóng)的基本原理
網(wǎng)頁(yè)請(qǐng)求的過(guò)程分為兩個(gè)環(huán)節(jié):
Request (請(qǐng)求):也就是向服務(wù)器發(fā)送訪(fǎng)問(wèn)請(qǐng)求。
Response(響應(yīng)):服務(wù)器在接收到用戶(hù)的請(qǐng)求后癣缅,會(huì)驗(yàn)證請(qǐng)求的有效性厨剪,然后向用戶(hù)(客戶(hù)端)發(fā)送響應(yīng)的內(nèi)容,客戶(hù)端接收服務(wù)器響應(yīng)的內(nèi)容友存,將內(nèi)容展示出來(lái)祷膳,就是我們所熟悉的網(wǎng)頁(yè)請(qǐng)求
網(wǎng)頁(yè)請(qǐng)求的方式也分為兩種:
GET:最常見(jiàn)的方式,一般用于獲取或者查詢(xún)資源信息屡立,也是大多數(shù)網(wǎng)站使用的方式直晨,響應(yīng)速度快。
POST:相比 GET 方式膨俐,多了以表單形式上傳參數(shù)的功能勇皇,因此除查詢(xún)信息外,還可以修改信息焚刺。
所以敛摘,在寫(xiě)爬蟲(chóng)前要先確定向誰(shuí)發(fā)送請(qǐng)求,用什么方式發(fā)送檩坚。B站是使用get請(qǐng)求的着撩,所以我們使用get方式抓取數(shù)據(jù)
獲取B站視頻搜索結(jié)果url列表
demand參數(shù):表示搜索視頻排序方式(watch:按播放量排序诅福,dm:按彈幕量排序,time:按上傳時(shí)間排序拖叙,collect:按收藏量排序)
kw參數(shù):表示搜索的視頻關(guān)鍵字列表
def bilibili_url (demand,kw):
if demand == 'watch':
url = 'https://search.bilibili.com/all?keyword={}&order=click&duration=0&tids_1=0'
elif demand == 'dm':
url = 'https://search.bilibili.com/all?keyword={}&order=dm&duration=0&tids_1=0'
elif demand == 'time':
url = 'https://search.bilibili.com/all?keyword={}&order=pubdate&duration=0&tids_1=0'
elif demand == 'collect':
url = 'https://search.bilibili.com/all?keyword={}&order=stow&duration=0&tids_1=0'
else :
url = 'https://search.bilibili.com/all?keyword={}'
url_list = []
for i in kw:
list_view = url.format(i)
url_list.append(list_view)
return url_list
信息爬取
def get_bilibili_play_info(url_list):
info = [] #建立一個(gè)空列表存放信息
seen = set() #建立一個(gè)空集合氓润,去除重復(fù)字典
for j in url_list:
for i in range(50):
t=random.randint(0,9)
time.sleep(t) #隨機(jī)sleep0到9秒防反爬
url=j + ('&page=%d' %(i+1)) #跳轉(zhuǎn)頁(yè)數(shù),B站支持顯示50頁(yè)搜索信息
print (url)
headers = {'Accept': 'text/html, application/xhtml+xml, image/jxr, */*',
'Accept - Encoding':'gzip, deflate',
'Accept-Language':'zh-Hans-CN, zh-Hans; q=0.5',
'Connection':'Keep-Alive',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063'}
bz_data = requests.get(url,headers=headers)
#添加headers參數(shù)偽裝瀏覽器反爬
soup = BeautifulSoup(bz_data.text,'lxml') #解析網(wǎng)頁(yè)信息
for link in soup.find_all(name='li',class_="video-item matrix"):
#對(duì)每一個(gè)視頻信息循環(huán)處理薯鳍,link的信息如下圖
info_dic = {}
for temp in link.find_all(name='a',class_="img-anchor"):
#提取a標(biāo)簽咖气,temp信息如下圖
info_dic['title'] = temp.get('title') #視頻名信息
info_dic['url'] = "http:%str" %temp.get('href') #視頻url信息
a = str(link.find('span',class_='so-icon watch-num'))
#a= '<span class="so-icon watch-num" title="觀(guān)看"><i class="icon-playtime"></i>\n 1230.1萬(wàn)\n </span>'
if '萬(wàn)' in a:
watch_num=re.findall(r"\d+\.\d+", a)
watch_num[0]=(float(watch_num[0]))*10000
info_dic['watch_num']=int(watch_num[0])
else :
watch_num=re.findall(r"\d+", a)
watch_num[0]=float(watch_num[0])
info_dic['watch_num']=int(watch_num[0])
a = str(link.find('span',class_='so-icon hide'))
#'<span class="so-icon hide" title="彈幕"><i class="icon-subtitle"></i>\n 5.8萬(wàn)\n </span>'
if '萬(wàn)' in a:
hide=re.findall(r"\d+\.\d+", a)
hide[0]=(float(hide[0]))*10000
info_dic['dm']=int(hide[0])
else :
hide=re.findall(r"\d+", a)
hide[0]=int(hide[0])
info_dic['dm']=int(hide[0])
a = str(link.find('span',class_='so-icon time'))
#'<span class="so-icon time" title="上傳時(shí)間"><i class="icon-date"></i>\n 2020-02-01\n </span>'
info_dic['upload_time']=re.findall(r"(\d{4}-\d{1,2}-\d{1,2})", a)[0]
#如果需要提取為datatime格式,可以用下面這條
#info_dic['上傳時(shí)間']=datetime.date(*map(int, time_list[0].split('-')))
t = tuple(info_dic.items())
if t not in seen:
#篩選是否有重復(fù)的視頻信息
seen.add(t)
info.append(info_dic)
return info
其中挖滤,每個(gè)視頻的網(wǎng)頁(yè)解析信息link結(jié)果如圖:數(shù)據(jù)入庫(kù)
參數(shù):host崩溪,user,password斩松,db為連接數(shù)據(jù)庫(kù)的參數(shù)
def rq(host,user,password,db,table,info):
cols = ", ".join('`{}`'.format(k) for k in info[0].keys())
#提取列表中字典的鍵伶唯,格式為`key1`,`key2`.....
val_list = []
for i in info:
#僅保留列表中所有字典的值,以元組格式重新存入新列表
val_tup = tuple(i.values())
val_list.append(val_tup)
cols_val = '%s'
for j in range(len(list(info[0].keys()))-1):
#根據(jù)鍵個(gè)數(shù)添加%s
cols_val = cols_val + ",%s"
sql="insert into %s(%s) value(%s)"
res_sql = sql % (table,cols,cols_val)
#res_sql ="insert into table(`key1`,`key2`...) value(%s,%s...)"
db=pymysql.connect(host=host,user=user,password=password,db=db,charset='utf8')
#連接數(shù)據(jù)庫(kù)
cursor = db.cursor()
cursor.executemany(res_sql,val_list)
#對(duì)數(shù)據(jù)庫(kù)進(jìn)行批量操作惧盹,其中val_list必須為以元組組成的列表格式
db.commit() # 提交更新的數(shù)據(jù)到數(shù)據(jù)庫(kù)
cursor.fetchall() #查詢(xún)處理后數(shù)據(jù)結(jié)果
print ('入庫(kù)完成乳幸!')
# 關(guān)閉cursor游標(biāo)
cursor.close()
#關(guān)閉服務(wù)器
db.close()