0.搜索关键词、点击搜索、进入新页面
1.获取每个页面的HTML
2.解析每个页面的HTML
3.将爬取到的数据写入csv文件
(这里搜索的例子是华晨宇,爬取前5页)
因为刚学爬虫,所以注释会写得比较多,方便自己理解。
文章末尾有完整源代码,分析过程的代码比较杂。
先引入相关模块、做好准备工作:
import requests
from bs4 import BeautifulSoup
from selenium import webdriver # 引入浏览器驱动
from selenium.webdriver.common.by import By # 借助By模块进行操作
from selenium.webdriver.support.ui import WebDriverWait # 显式等待
from selenium.webdriver.support import expected_conditions as Ec # 显式等待的条件
from selenium.common.exceptions import TimeoutException # 捕获超时异常
import pandas as pd
browser = webdriver.Chrome() # 获取浏览器对象
WAIT = WebDriverWait(browser,10) # 指定最长等待时间
url = 'https://www.bilibili.com'
print('开始访问b站……')
browser.get(url)
首先我们按F12观察一下b站页面的搜索框和搜索按键:
这里我们使用XPath选取节点,右键点击框起来的标签选择Copy XPath。
def search(content):
print('正在进行搜索……')
# 用XPath选取节点
input = WAIT.until(Ec.presence_of_element_located((By.XPATH,'//*[@id="nav_searchform"]/input')))
submit = WAIT.until(Ec.element_to_be_clickable((By.XPATH,'//*[@id="nav_searchform"]/div/button')))
# 从搜索框输入并点击搜索
input.send_keys(content)
submit.click()
首先我们搜索华晨宇,打开第二页:
我们看到在URL的末尾有一个关于页数的标记,所以我们使用for循环配合f+字符串的语句来格式化:
for i in range(5):
# 用f+字符串来表示每一个页面的网址
url = f"https://search.bilibili.com/all?keyword=%E5%8D%8E%E6%99%A8%E5%AE%87&from_source=nav_search_new&page={str(int(i+1))}"
这个步骤的代码为:
def crawl():
htmls = [] # 存放每个页面的HTML
# 用for循环爬取每一个页面并获得其HTML
for i in range(5):
# 用f+字符串来表示每一个页面的网址
url = f"https://search.bilibili.com/all?keyword=%E5%8D%8E%E6%99%A8%E5%AE%87&from_source=nav_search_new&page={str(int(i+1))}"
r = requests.get(url) # 返回Response对象
if r.status_code != 200: # 状态码检测
raise Exception("error")
htmls.append(r.text) # r.text是字符串类型
return htmls
这时我们已经完成了搜索的操作,接下来按F12:
然后找到每个视频的HTML内容,我们使用find和find_all函数进行提取(返回的是列表):
def parse(htmls):
videos = [] # 存放每个视频解析出来的HTML
print('解析页面中……')
for html in htmls:
soup = BeautifulSoup(html, 'html.parser') # 解析每个页面
# 获取每个视频的标签树
video = soup.find(class_ = "video-list clearfix").find_all(class_="video-item matrix")
videos.extend(video) # 列表存入列表,所以用extend()函数
接下来查找每个视频的名称、地址、简介、观看次数、弹幕数量及发布时间的HTML内容:
我们以第一个视频为例,可以看出视频的名称是a标签的title属性,地址是href属性,简介在有属性class="des hide"的标签的NavigableString(非属性字符串)元素中,而观看次数、弹幕数量及发布时间也类似,这里我找到class=“so-icon watch-num”、class=“so-icon time”、class="so-icon hide"对应的标签并用get_text()的方法获取目标路径下的子孙字符串(或者你找到i标签,用.string方法获取也可以,这里尝试多一种方法)。
完整的parse函数代码:
def parse(htmls):
videos = [] # 存放每个视频解析出来的HTML
print('解析页面中……')
for html in htmls:
soup = BeautifulSoup(html, 'html.parser') # 解析每个页面
# 获取每个视频的标签树
video = soup.find(class_ = "video-list clearfix").find_all(class_="video-item matrix")
videos.extend(video) # 列表存入列表,所以用extend()函数
items = [] # 存放每个视频的各个项目
print('正在爬取相关信息……')
for video in videos:
item = {} # 每个字典存放每个视频的相关信息
item['视频标题'] = video.find('a')['title'] # 获取标签属性
item['视频地址'] = video.find('a')['href']
item['简介'] = video.find(class_ = 'des hide').string # 获取NavigableString
item['观看次数'] = video.find(class_ = 'so-icon watch-num').get_text() # 获取目标路径下的子孙字符串
item['发布时间'] = video.find(class_ = 'so-icon time').get_text()
item['弹幕数量'] = video.find(class_ = 'so-icon hide').get_text()
items.append(item) # 字典存入列表,所以用append()函数
return items
这里用pandas库的DataFrame方法构造一个数据框,然后写入csv文件:
def save_to_csv(items):
print('成功将数据写入文件!')
# 将爬取的数据写入csv文件
df = pd.DataFrame(items) # 用DataFrame构造数据框
df.to_csv("华晨宇.csv")
def main():
try:
search('华晨宇')
htmls = crawl()
items = parse(htmls)
save_to_csv(items)
finally:
print('爬取信息成功!')
browser.close()
if __name__ == '__main__':
main()
https://github.com/Giyn/Spider
爬取成功!接下来我们看一下我们的csv文件:
打开它:
发现显示乱码……
我们用记事本打开该csv文件:
然后另存为,同时编码方式选择带有 BOM 的 UTF-8:
再次打开该csv文件,乱码问题就解决了:
https://github.com/Giyn/Spider