当前位置:首页 > 新闻动态 > 网站文章

Python爬取豆瓣电影中国医生,爬虫之路,永无止境(附源码)

来源: 浏览:113 时间:2023-08-08

用Python爬取中国医生电影评论生成词云并实现数据可视化(内附源码)

今日目标:爬取豆瓣电影中国医生评论实现数据可视化

前言

前段时间,主旋律电影《中国医生》上线,大家有没有去看呢?反正我去看了,有点感人(其实哭得稀里哗啦)。看完后,内心感概万分,回到家缓了好久。看了电影的海报画面,想着可以爬一爬大家对电影的评论,正好给大家写文了。于是,利用下班时间给大家做了这个爬虫项目。废话不多了,直接开整。

工具使用

开发环境

系统:Windows10 64位

Python版本:Python3.7

IDE:Pycharm

第三方库:selenium lxml re wordcloud PIL numpy jieba matplotlib

代码演示

饼图

词云生成

柱状图生成

项目思路分析

1 获取网址规律

可以看到 start 和 status是变化的关键

在这个页面可以通过xpath获取地址网页 评论详情 和评分

地址网页:

评论详情:

评分 :

2 打开地址网页,获取地址信息

有些没有地址信息 所有需要获取全部 后期通过数据处理获取有地址信息的

3 把获取的数据进行解析 分别存放三个文件

4 通过读取相应的文件生成相应的图表

项目难点分析

1 滑块验证

这个参考代码里面的滑块验证的方法

通过像素对比找到缺口

移动一段距离 之后速度变化慢慢往前面走 到达缺口就能够验证成功

2 切换iframe

# 切换iframe
driver.switch_to.frame(1)

需要切换iframe才能找到滑块和输入账号 密码 的元素位置

3 显示等待

wait.until(
EC.presence_of_element_located((By.XPATH, '//*[@id="profile"]/div/div[2]'))
)

等待元素出现才进行下一步的处理

4 数据处理

出现不合法的数据 比如 该用户已经主动注销帐号

判断源代码中是否存在 如果存在就跳过 防止等待时间过长 退出程序

if driver.page_source.__contains__("`该用户已经主动注销帐号`"):continue

5 抓取思维

采取先抓大再抓小

这样可以确定几个元素是相对应的

循环的时候会再次使用xpath 此时的xpath写法

需要加一个 .

item = {
"href": email_detail.xpath('./div/a/@href'),
"detail": email_detail.xpath("./div[2]/p/span//text()"),
"rate": email_detail.xpath("./div[2]/h3/span[2]/span[2]/@class")
}

6 json模块默认编码

默认:ascii码 为了中文的正确显示 需要传递参数ensure_ascii

f.write(json.dumps(email, ensure_ascii=False) + ",
")

7 词云图的生成

按照图片格式只需要添加一个参数即可mask

# graph 图片对象
word_cloud = WordCloud(font_path="simsun.ttc",
background_color="white",
mask=graph, # 指定词云的形状
)

8 柱状图和饼图

难点在于json文件读取

循环获取的data 然后通过列表构建echarts需要的数据格式

$.getJSON("json_file/rate.json", function(data) {
var x_data = [];
for (var i = 0; i < data.length; i++) {
console.log(data[i]);
for(var key in data[i]){ // 输出字典元素,如果字典的key是数字,输出时会自动按序输出
hello = {value: data[i][key], name: key+"星"}
x_data[i] = hello;
}
};

源码展示

词云图

# 词云生成
import jieba
import numpy as np
from wordcloud import WordCloud
import matplotlib.pyplot as plt
from PIL import Image
text = open("json_file/detail.text",encoding='utf8').read()
text = text.replace('
',"").replace("u3000","")
text_cut = jieba.lcut(text)
text_cut = ' '.join(text_cut)
# 主要区别
background = Image.open("image/mmexport1626868510673.jpg")
graph = np.array(background)
word_cloud = WordCloud(font_path="simsun.ttc",
                       background_color="white",
                       mask=graph, # 指定词云的形状
                       )
word_cloud.generate(text_cut)
plt.subplots(figsize=(12,8))
plt.imshow(word_cloud)
plt.axis("off")
plt.show()

滑块验证

def slide(driver):
    """滑动验证码"""
    # 切换iframe
    driver.switch_to.frame(1)
    # 找到滑块
    block = driver.find_element_by_xpath('//*[@id="tcaptcha_drag_button"]')
    # 找到刷新
    reload = driver.find_element_by_xpath('//*[@id="reload"]')
    while True:
        # 摁下滑块
        ActionChains(driver).click_and_hold(block).perform()
        # 移动
        ActionChains(driver).move_by_offset(180, 0).perform()
        # 获取位移
        tracks = get_tracks(30)
        # 循环
        for track in tracks:
            # 移动
            ActionChains(driver).move_by_offset(track, 0).perform()
        # 释放
        ActionChains(driver).release().perform()
        # 停一下
        time.sleep(2)
        # 判断
        if driver.title == "登录豆瓣":
            print("失败...再来一次...")
            # 单击刷新按钮刷新
            reload.click()
            # 停一下
            time.sleep(2)
        else:
            break

打开文件获取清洗数据

def get_file():
    # 打开文件获取清洗数据
    f = open('data.json', 'r', encoding="utf-8")
    hello = f.read()
    # 地址列表 评分列表 评论内容列表
    address_ = []
    rate_ = []
    detail_ = []
    # 按换行符进行
    for i in hello.split("
"):
        try:
            # 转换为字典
            data = eval(i[:len(i) - 1])
            # 获取评论详细内容
            detail = data.get("detail")[0]
            # 获取评分
            rate = re.findall("(d{1})d", data.get("rate")[0])[0]
            # 获取地址
            address = ".".join(re.findall("常居: ([u4e00-u9fa5]{2})", "".join(data.get("address"))))
            # 地址存在则添加到地址列表中
            if address:
                address_.append(address)
            # 把评分添加到评分列表中
            rate_.append(rate)
            # 把评论添加到评论列表中
            detail_.append(detail)
        except:
            continue

获取评分和唯一地址

# 获取评分和地址的唯一
    rate_set = set(rate_)
    address_set = set(address_)
    # 写入 评分json
    with open("json_file/rate.json", 'w', encoding="utf-8") as file:
        rate_json_list = []
        for r in rate_set:
            # 获取评分频次并且构造字典
            rate_json = {r: rate_.count(r)}
            rate_json_list.append(rate_json)
        file.write(json.dumps(rate_json_list, ensure_ascii=False))

写入地址json文件

# 写入地址json文件
    with open("json_file/address.json", 'w', encoding="utf-8") as file:
        address_json_list = []
        for a in address_set:
            # 获取地址频次并且构造字典
            address_json = {a: address_.count(a)}
            address_json_list.append(address_json)
        file.write(json.dumps(address_json_list, ensure_ascii=False))

写入详情文件夹

# 写入详情文件夹
    with open("json_file/detail.text", 'w', encoding="utf-8") as file:
        detail = "".join(detail_).replace("
", "")
        file.write(detail)
def get_json(data, driver, f):
    Html = etree.HTML(data)
    # 通过xpath获取响应的板块
    email_details = Html.xpath('//*[@id="comments"]/div')
    items = []
    # 循环板块 获取每一个板块里面的数据
    for email_detail in email_details:
        # 地址页的地址  评论内容 评分呢
        item = {
            "href": email_detail.xpath('./div/a/@href'),
            "detail": email_detail.xpath("./div[2]/p/span//text()"),
            "rate": email_detail.xpath("./div[2]/h3/span[2]/span[2]/@class")
        }
        items.append(item)

循环列表获取地址网址

 # 循环列表获取地址网址 爬取地址
    for email in items:
        # 如果网址为控则跳过
        if not email.get("href"):
            continue
        driver.get(email.get("href")[0])
        # 如果评论用户已经注册则跳过
        if driver.page_source.__contains__("该用户已经主动注销帐号"):
            continue
        print(email.get("href"))
        wait = WebDriverWait(driver, 10)
        # 隐士等待 等待数据板块出现进行下一步
        wait.until(
            EC.presence_of_element_located((By.XPATH, '//*[@id="profile"]/div/div[2]'))
        )
        # 获取地址
        email["address"] = etree.HTML(driver.page_source).xpath('//*[@id="profile"]/div/div[2]/div[1]/div//text()')
        # 整体数据写入初始文件中
        f.write(json.dumps(email, ensure_ascii=False) + ",
")

主程序

def main(user, pwd):
    """主程序"""
    f = open("data.json", "w", encoding="utf-8")
    f.write("[")
    url = "https://accounts.douban.com/passport/login"
    # 驱动路径
    driver_file = os.path.join(os.path.dirname(__file__), "driver_exe", "chromedriver.exe")
    # 声明驱动
    driver = webdriver.Chrome(executable_path=driver_file)
    driver.get(url)
    driver.find_element_by_xpath('//*[@id="account"]/div[2]/div[2]/div/div[1]/ul[1]/li[2]').click()
    driver.find_element_by_xpath('//*[@id="username"]').send_keys(user)
    driver.find_element_by_xpath('//*[@id="password"]').send_keys(pwd)
    driver.find_element_by_xpath('//*[@id="account"]/div[2]/div[2]/div/div[2]/div[1]/div[4]/a').click()
    # 停一下,等待出现
    time.sleep(2)
    # 滑动验证码
    slide(driver)
print("成功")
    type_mile = ["P", "F"]
for type_ in type_mile:
        if type_mile == "P":
            index_mile = 480
        else:
            index_mile = 180
        for i in range(0, index_mile, 20):
            url = f"https://movie.douban.com/subject/35087699/comments?start={i}&limit=20&status={type_}&sort=new_score"
            print(url)
            driver.get(url)
            # 解析数据
get_json(driver.page_source, driver, f)
    f.write("]")
driver.quit()
    get_file()
    # 所有数据获取完毕之后推出浏览器 解析数据
    # 解析完毕并且创建解析之后数据的json文件
    f.close()
if __name__ == '__main__':
    user = input("请输入你的账号:")
    pwd = input("请输入你的密码:")
    main(user, pwd)

仅供学习,爬虫使用需谨慎!

希望可以得到各位的一键三连,感谢各位支持!

祝大家学习python顺利!

如果有正在跟我一样的自学的朋友,需要我本篇的代码或者其他的Python学习资料可以转发此文私信我(私信发我“中国医生”

地址 · ADDRESS

地址:建邺区新城科技园嘉陵江东街18号2层

邮箱:309474043@qq.Com

点击查看更多案例

联系 · CALL TEL

400-8793-956

售后专线:025-65016872

业务QQ:309474043    售后QQ:1850555641

©南京安优网络科技有限公司 版权所有   苏ICP备12071769号-4  网站地图