【爬虫】爬取某乎文章(含图片)

该文介绍了一个Python项目,利用Selenium模拟浏览器行为抓取知乎用户主页的文章,包括内容和图片,因TXT和CSV无法存储图片,故选择存储为Markdown格式。在处理过程中,遇到HTML转PDF的困难,以及需要处理图片URL,最终成功保存了大部分文章。
摘要由CSDN通过智能技术生成

1.目的

通过selenium爬取zhihu用户主页下的文章(含图片)并存储为md文件。

为什么存储为md文件?因为一般的像txt,csv文件是不能存储图片的,而md文件是可以的并且可以支持html标记的。为什么不存储为pdf文件?是因为我的pycharm中打开html文件直接报404错误,所以本来我是优先存储为pdf文件的。

2.思路

①通过selenium进行翻页获取完所有文章的详情页url并存储到一个列表

②通过requests遍历列表中的url获取详情页源代码

③在源代码中通过xpath获取我们想要的内容的目标代码(含图片url)和标题

④在目标代码中把图片的无效url替换成有效url

⑤把目标代码转化为html并存为md文件

这里关于登录的问题考虑到其他因素就没使用打码平台,所以要用手机扫码登录。

3.部分代码详解

①知乎会检测到selenium,所以这段代码是避免selenium被检测到

# 使用chrome开发者模式
    self.options = webdriver.ChromeOptions()
    self.options.add_experimental_option('excludeSwitches', ['enable-automation'])
    # 禁用启用Blink运行时的功能
    self.options.add_argument("--disable-blink-features=AutomationControlled")
    # Selenium执行cdp命令  再次覆盖window.navigator.webdriver的值
    self.driver = webdriver.Chrome(options=self.options)
    self.driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
        "source": """
                        Object.defineProperty(navigator, 'webdriver', {
                          get: () => undefined
                        })
                       """
    })

②通过requests获取到的源代码的图片为

c847eebcb8a746e7820d924adeba1582.png

所以要把图片url替换为有效的url 

 # 有效的图片链接
 img_url = re.findall('<noscript><img src="(.*?)" data-caption=""', data)
 # 要替换的
 repl = re.findall('</noscript><img src="(.*?)" data-caption=""', data)
 # 把无效链接替换成有效链接
 for r, i in zip(repl, img_url):
     data = data.replace(r, i)

③保存为md文件

# 把目标代码转换成html
html = etree.tostring(data_source, encoding='utf-8').decode('utf-8')
# 储存在md文件
f = open(f'./文件/{title}.md', 'w', encoding='utf-8')
f.write(html)

4.源码

import os
import re
import time
import requests
from lxml import etree
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


class Zhihu:
    def __init__(self):
        # 使用chrome开发者模式
        self.options = webdriver.ChromeOptions()
        self.options.add_experimental_option('excludeSwitches', ['enable-automation'])
        # 禁用启用Blink运行时的功能
        self.options.add_argument("--disable-blink-features=AutomationControlled")
        # Selenium执行cdp命令  再次覆盖window.navigator.webdriver的值
        self.driver = webdriver.Chrome(options=self.options)
        self.driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
            "source": """
                            Object.defineProperty(navigator, 'webdriver', {
                              get: () => undefined
                            })
                          """
        })
        self.driver.maximize_window()
        # 用来存储所有详情页url
        self.detail_url = []
        self.head = {
            'User-Agent': '自己的ua',
            'Cookie': '自己的cookie',
            'Referer': 'https://www.zhihu.com/people/lao-q-84/posts'
        }

    # 登录,在这里用手机扫码登录
    def Denglu(self):
        self.driver.get('https://www.zhihu.com/signin?next=%2Fhot')
        WebDriverWait(self.driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, '//div[@class="SignFlow-tab"]'))
        )
        print('登录窗口加载完毕!!!')
        WebDriverWait(self.driver, 10).until(
            EC.element_to_be_clickable((By.ID, 'Popover53-toggle'))
        )
        print('登陆成功!!!')
        time.sleep(3)
        # 搜索用户
        self.driver.find_element(By.XPATH, '//div/label/input[@type="text"]').send_keys('程序猿鱼皮', Keys.ENTER)
        WebDriverWait(self.driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, '//div[@class="Card-headerText"]'))
        )
        print('搜索成功!!!')
        time.sleep(3)

        self.driver.find_element(By.XPATH, '//*[@id="root"]/div/main/div/div[1]/div/div/ul/li[2]/a').click()
        time.sleep(2)

        # 点击用户进入主页
        self.driver.find_element(By.XPATH,
                                 '//*[@id="SearchMain"]/div/div/div/div/div[2]/div/div/div/div[2]/h2/span/div/span/div/a/span/em').click()
        time.sleep(2)
        # 切换窗口
        self.driver.switch_to.window(self.driver.window_handles[1])
        time.sleep(2)

        # 点击进入文章主页
        self.driver.find_element(By.XPATH, '//*[@id="ProfileMain"]/div[1]/ul/li[5]/a').click()
        WebDriverWait(self.driver, 10).until(
            EC.element_to_be_clickable((By.XPATH, '//h4[@class="List-headerText"]'))
        )
        print('进入文章主页成功!!!')
        time.sleep(4)
        self.get_url()

    # 获取文章url
    def get_url(self):
        for i in range(8):
            try:
                print(f'翻页成功,当前第{i + 1}页!!!')
                self.driver.execute_script('window.scrollTo(0,document.body.scrollHeight)')
                time.sleep(2)
                # 获取详情页链接
                url_list = self.driver.find_elements(By.XPATH, '//h2/span/a')
                # 把详情页存储到列表
                for u in url_list:
                    new_url = u.get_attribute('href')
                    self.detail_url.append(new_url)

                # 点击翻页
                self.driver.find_element(By.XPATH,
                                         '//div[@class="Pagination"]/button[@class="Button PaginationButton PaginationButton-next FEfUrdfMIKpQDJDqkjte Button--plain fEPKGkUK5jyc4fUuT0QP"]').click()
                time.sleep(3)
            except:
                print('已经是最后一页了!!!')
        self.get_info()

    # 把文章存储为md文件
    def get_info(self):
        if not os.path.exists('./文件'):
            os.mkdir('./文件')
        for url in self.detail_url:
            print('获取到的详情页链接:', url)
            response = requests.get(url=url, headers=self.head)
            data = response.text

            # 把有图片的文章中的<img src="######" 这里的链接替换为有效链接
            try:
                # 有效的图片链接
                img_url = re.findall('<noscript><img src="(.*?)" data-caption=""', data)
                # 要替换的
                repl = re.findall('</noscript><img src="(.*?)" data-caption=""', data)
                for r, i in zip(repl, img_url):
                    data = data.replace(r, i)
            except Exception as e:
                print('我是没图片的文章---', e)
                print()

            # 保存图片
            try:
                res = etree.HTML(data)
                # 我们想要的文章内容
                data_source = res.xpath('//div[@class="css-1yuhvjn"]')[0]
                # 标题
                title = res.xpath('//h1[@class="Post-Title"]/text()')[0]
                # 再把data_source转换成html
                html = etree.tostring(data_source, encoding='utf-8').decode('utf-8')
                # 储存在md文件
                f = open(f'./文件/{title}.md', 'w', encoding='utf-8')
                f.write(html)

                print(f'{title}---保存成功!!!')
                print()
            except Exception as e:
                print(e)
                print()


if __name__ == '__main__':
    z = Zhihu()
    z.Denglu()

5.结果展示

cc4bfb08c31b4b3792fc76e241593baa.png

 6fc9b4664b2b40d38bbfb7c606842754.png

 最后146篇文章保存了142篇,剩下几篇我觉得是文章标题的问题。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值