python爬虫的学习总结

2023-11-04

背景

基于django框架完成jira网页数据的爬取。由于对爬虫知识知道的太少,我开始了新的学习之旅。本文前半部分都是记录这一周主要的错误,如果想直接看最终成果,可以跳到本文“成功爬取”部分浏览。

学习爬虫知识

在知道了本项目可能需要爬虫后,我开始学习爬虫相关知识,通过对爬虫教程的阅读实践修改,我初步理解了一些相关的语法:

  1. 什么时候可以使用爬虫:但且仅当符合robots.txt时可以。
  2. 以get方式爬取:request.get(url),返回response,使用response.text输出html文件
  3. 以post方式爬取:调用开发者模式,选取network,查找来往文件,取出url和formdata中的内容组成新的字典,request.post(url,formdata),并用json解析,即可获得数据。
  4. 问题:原文中的链接已经不让爬取了,get的我是用baidu,post可以参考其他文章,将url中的_o去掉或者实时生成参数。

至此爬虫学习部分就暂时告一段落,截图就不放了,真正的困难还在后面

了解jira

jira,Atlassian公司出品的项目与事务跟踪工具,被广泛应用于缺陷跟踪、客户服务、需求收集、流程审批、任务跟踪、项目跟踪和敏捷管理等工作领域。我司主要用于测试提单的提交工作,但提单的数量十分庞大,一个一个查询十分不方便,因此我的任务就是爬取网站数据,并进行分析,存入数据库,并在我们自己的网页上更清晰明了的完成显示。

对API的尝试

和同事探讨了解jira后,我知道了jira有自己的API,只需要替换成我司的网站就可以爬取数据了,但很可惜,经过两天的尝试,除了几个API可以返回结果,其余的都是返回404或者json解析错误。没法得到数据。基于之前学习的一些爬虫知识,我开始转向打算使用正常原始的爬虫方法爬取数据,也就是本文的重点,也是一切问题的开始。。。
注:另一位成员则继续学习相关API,并发现python有更好封装的jira库,这个库已经证实可以爬取该网站,并且效率方面更优(因为正常爬取需要一条数据一条数据的爬取,自然不如直接查询数据库获得全部数据来的快,但基于本文方法更为通用,易于后人爬取其他网站,因此我们两个决定同步完成爬取代码的编写)

原始方法爬取

最开始的代码由于失败,已经找不到了,这也是一个教训吧,以后写代码的时候会注意按版本顺序编写,而不是失败后直接就覆盖掉。现在凭借记忆完成描述吧。
首先遇到的一大困难就是jira网站需要登录,这也是后续诸多问题的三大核心来源之一。直接爬取会出现直接跳转到登录界面,而无法获取信息。由于原始爬取的方法记忆性较差,因此在学习了cookie之后,我开始尝试使用cookie进行登录,参考模拟登录这篇文章,在真实登录过网站后,在开发者模式下选择network,查找来往文件,提取头部含有cookie的文件,复制cookie和user_agent,并将他们加入头部,很顺利的让我进入网站,获取了相关信息。但是好景不长,在第二天关机重启后,我再次运行,却发现cookie已经发生了改变,这种方式无法实现长久的登录,最终也只好放弃。(我后来也想过每次模拟登录一下获取cookie然后就可以了,但当时我已经知道了selenium,就没有费事搞了)
另一个核心问题就是jira网站的加载方式问题,访问首页时浏览器可以看到提单列表和提单内容,但爬虫爬不到这些数据,经过后期诸多尝试猜测,我认为这是由于加载顺序问题造成的,实际上,jira是分许多个文件依次加载各个模块,而一般原始爬虫代码在接受到一个response后就不会继续接受response了,也不会去拼接这些网站,所以无法爬到完整的数据,因为虽然以提单的id爬虫可以获取到对应的数据,但我却无法很好的获取我要爬的链表。也是因为如此,我开始新的一轮搜索,尝试找到如何应对这种多个response的情况,知道我在知乎上意外看到了selenium这个单词。

学习selenium

selenium是一个完全仿真用户真实运行的工具,可以完成链接的点击,文本框输入,读取数据,甚至拖拽等动作。如此岂不美哉嘛。那我还何必担心会出现登录问题,我直接代码帮我登录就好了嘛。
说干就干,我用两分钟扫了一眼别人的博客,复制运行一下代码完成简单的学习使用。这里我主要参考的是selenium实例,同时根据错误方法进行修改,符合我的环境的语法。有一个不太好的地方就是网上资源大都是针对Chrome或者火狐的,当然Edge经过实践也是完全可以用的,只不过参数需要自己调一下。比如它的options是要用

from selenium.webdriver.edge.options import Options
options = Options()
browser=webdriver.Edge(options = options)

很快我的第一个登录程序就写完了,这个代码还能找回来,就隐去一些信息放出来吧。

try:
        browser=webdriver.Edge(options=options)
        browser.get("要访问的网站")
        input1=browser.find_element(By.ID,value="login-form-username")
        input2=browser.find_element(By.ID,value="login-form-password")
        input1.send_keys("your_user_name")
        input2.send_keys("your_password")
        button=browser.find_element(By.ID,value="login-form-submit")
        button.click()
        browser.implicitly_wait(10)
        print(browser.find_element(By.XPATH,'要爬取数据的XPATH路径').text)
        lis = browser.find_elements(By.CLASS_NAME,'splitview-issue-link')
    finally:
        browser.close()

就这样我分别通过id,class,xpath完成登录并精准获取第一个提单的提交信息和创建时间,但与此同时,我一直不敢面对的第三大核心问题也随之出现了。。。怎么访问近两个月的数据? 相信有人肯定会说直接爬嘛,都知道提单列表了。但别忘了,两个月的提单,就单纯我们组就有数十个,一个一个爬取起码要一两分钟,绝对不可取的。另一个同学用jira API可以直接一次获得,我真实爬取却要依次进行,当然啦,其实也就第一次爬要这么久,以后都爬变化的就好了。但我还是抱有一丝侥幸,觉得多线程可以解决我的问题,在加上我还没有写过python的多线程,所以何不就此学学呢?哪知一入佛门深似海,让我仿佛一切都回到了原点。

多线程加速

最开始我决定参考这篇selenium多线程加速,于是改动代码先开一个线程去访问链表中的一个元素试试,但随后发现一个让我很不爽的问题,由于我要多线程操作多个界面,所以肯定是要开多个新的窗口去访问,可谁知开新的窗口居然不能保存登录状态,直接跳转到登录链接,这可不好啊,如果每次都要登录岂不是太过麻烦(可以开几个线程登录上,然后这几个线程分布访问一部分数据,当然这是后话,我当时想的首先就是保存登录信息)。那这个简单,认证我是不是登录过只需要我的cookie对就可以了嘛,因此我就在网上搜索添加cookie的方法。但不知道是版本原因还是什么情况,加上cookie依然不可以自动登录。我又尝试勾选浏览器自带的记住本电脑,但依然无果。我猜想可能是因为记住本电脑也只是去记录cookie,并不是记录电脑的信息,所以开新的网页时依然会发生要输入密码的问题。

保存浏览器信息

首先搜索的是这篇博客保存浏览器信息,亲测有效果但对于多线程很不友好,每次只能开一个窗口新打开的窗口覆盖原来的,如果开多线程的话会发生多个线程访问的是一个数据,但对于开多个端口的想法测试过,但目前还没有尝试完整,但只是在代码中开多线程启用cmd运行是不对的,最后会发生链接不上,用Telnet测试也是如此,只有某一个端口可以找到。
注:这里补充一些python运行cmd的知识,使用

import os
os.system(your_cmd)

这里要注意的是如果出现路径带有空格,cmd解析会出现问题,必须要把路径用引号引起来才可以。

成功爬取

经过一周的试错,我最终选择了一个目前来看最优的结果。首先创建若干个线程,让每个线程登录,并最终获取列表,在列表中给每个线程分配相同数量的链接任务,并各自打开对应链接的标签(如果是窗口会需要再次登录)。最后获取需要的数据。话不多说,上代码(本代码为安全保密起见,增加了一些数据的隐藏(所有中文部分),无法运行,主要是为了看思路,关键步骤已经增加了注释)

# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.edge.options import Options
from selenium.webdriver.edge.service import Service
from threading import Thread
import subprocess,threading,time
import time,sys,os,copy

thread_list = []
process_list = []
thread_num = 2
process_num = 2
last_herf = "最后要爬取的数据(链接),也可以改变stop去停止程序运行"
stop = 0
create_list = []
lock = threading.RLock() #互斥锁

def create(href,browser):
    href = 'window.open("' + href + '")' 
    print(href)
    browser.execute_script(href) #打开新标签
    browser.implicitly_wait(10)
    browser.switch_to.window(browser.window_handles[-1])
    temp = browser.find_element(By.XPATH,'要获取信息的路径').text
    print(temp)
    lock.acquire()
    create_list.append(temp) #保存
    lock.release()
    browser.switch_to.window(browser.window_handles[0])

def get_jira(num):
    try:
        options = Options()
        # 处理SSL证书错误问题
        options.add_argument('--ignore-certificate-errors')
        options.add_argument('--ignore-ssl-errors')
         
        # 忽略无用的日志
        options.add_experimental_option("excludeSwitches", ['enable-automation', 'enable-logging'])
        options.add_argument('--no-sandbox')
        options.add_argument('--disable-dev-shm-usage')
        options.add_argument('--headless') #无窗口模式运行
        options.add_argument('blink-settings=imagesEnabled=false')
        options.add_argument('--disable-gpu')
        browser=webdriver.Edge(options=options)
        browser.get("你首先要访问的网站(登录网站)")
        browser.implicitly_wait(10)
        input1=browser.find_element(By.ID,value="用户名标签")
        input2=browser.find_element(By.ID,value="密码标签")
        input1.send_keys("你的用户名")
        input2.send_keys("你的密码")
        button=browser.find_element(By.ID,value="登录标签")
        button.click()
        browser.implicitly_wait(10)
        button=browser.find_element(By.XPATH,value='获取所有信息链接的路径')
        button.click() #跳转以进入根网页
        browser.implicitly_wait(10)
        # print_log = open("printlog.html","w")
        # sys.stdout = print_log
        # print(browser.page_source)
        lis = browser.find_elements(By.CLASS_NAME,'获取所有信息形成的链表') #链表用elements
        for i in range(len(lis)):
            # print(lis[i].get_attribute("href"),num)
            if i%thread_num == num: #等额分配任务
                create(lis[i].get_attribute("href"),browser)
            if lis[i].get_attribute("href") == last_herf or stop:
                break
    finally:
        browser.close()

def start_prog():
    for i in range(thread_num): #创建线程
        t = Thread(target=get_jira,args=(i,))
        t.start()
        thread_list.append(t)
    for i in thread_list:
        i.join()

if __name__ == '__main__':
    time_start = time.time()
    start_process()
    for i in create_list:
        print(i)
    time_end = time.time()
    time_c = time_end - time_start
    print('time_cost', time_c, 's')

实际运行

经过我司网站实际爬取的成果显示,爬取32条数据时,使用2-4线程(进程)效果较好,这是因为虽然开多线程(进程)对IO密集型程序来说可以很好的提高速度,但是由于开多线程(进程)时,每次都需要登录(这是我最想取消的一步,但很可惜,始终没有找到一个完美的解决方案)会浪费大量时间。而且根据测试结果得出,jira服务器可能对多线程(进程)实现的不是很优良,以至于我在开多线程时,访问时间并不会明显缩短,相比于单线程,测试表现中,双线程可以节约1/3的时间,四线程可以节约接近1/2时间,8线程可以节约略多于1/2的时间。但开4线程以上的线程时,登录跳转时间已经可以占总运行时间一半以上。因此2-4线程时加速效果比较好,可以加速30%左右。当然,在爬取数据更多时,比如爬取100个数据,得到的结果是:相比于单线程,测试表现中,相比于单线程,双线程可以节约1/2时间,4线程可以节约2/3时间,6线程可以节约5/7时间,8线程可以节约约5/7时间。再计算进入登录过程的时间,可以得出6线程的性能略好于4线程,但性能差异不大。因此使用4-6线程对于爬取100数据来说效果较好。
综上所述,jira对于多线程(进程)有一定的实现,但效果不佳,存在瓶颈上限,当浏览线程数超过2时,性能提升度逐渐下降,4线程并不能达到4倍的提升速率,超过8时,浏览器无法正常运行。在我打开任务管理器进行监控时发现,2线程时的CPU占有率已经达到80%左右。之所以后期有所继续提升,应该是因为本程序介于IO密集型和CPU密集型程序吧。

进一步提速

由于爬虫最后是为了获取数据的,那么加载图片、css和JavaScript等用处就不是很大了,可以参考博客selenium加速爬取来完成加速,实测加速效果可以提速约1.2倍的样子。
当然这还没有结束,由于网页时分层加载的,所以不需要像上面代码所写的使用隐式等待,等到所有html都加载完毕才获取数据,可以使用显式等待。至此,爬虫的爬取速率已经得到了尽可能大的提升。

反思

本次项目至此已经完成了爬取核心部分,做一个简单的移植即可在已有框架中实现了。整个过程充满了跌宕起伏,但也学到了许多东西。对后来人也是一个很好的模板(例如多线程,多进程的使用,以后爬取时可以直接更换网页和对应的查找元素即可)。

怕什么真理无穷,进一寸有一寸欢喜。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python爬虫的学习总结 的相关文章

  • 在Python中,如何将矩阵逆时针旋转90度?

    gt gt gt def rotate matrix k List List int For example if I have m 1 2 3 2 3 3 5 4 3 rotate matrix m should give me 3 3
  • Flask 中“缺少 CSRF 令牌”,但它在模板中呈现

    问题 当我尝试登录 使用 Flask login 时 我得到Bad Request The CSRF session token is missing但令牌正在呈现 在模板中 secret key 已设置 并且我在本地运行localhost
  • 来自 pandas 数据帧的烛台图,用日期替换索引

    此代码给出了带有移动平均线的烛台图 但 x 轴位于索引中 我需要 x 轴位于日期中 需要做什么改变 import numpy as np import pandas as pd import matplotlib pyplot as plt
  • 将 Python Pandas DataFrame 写入 Word 文档

    我正在努力创建一个使用 Pandas DataFrames 的 Python 生成的报告 目前我正在使用DataFrame to string 方法 但是 这会作为字符串写入文件 有没有办法让我实现这一目标 同时将其保留为表格 以便我可以使
  • 查找正在导入哪些 python 模块

    从应用程序中使用的特定包中查找所有 python 模块的简单方法是什么 sys modules是将模块名称映射到模块的字典 您可以检查其键以查看导入的模块 See http docs python org library sys html
  • Django 多对多关系(类别)

    我的目标是向我的 Post 模型添加类别 我希望以后能够按不同类别 有时是多个类别 查询所有帖子 模型 py class Category models Model categories 1 red 2 blue 3 black title
  • 为什么 re.findall 在查找字符串中的三元组项时不具体。 Python

    所以我有四行代码 seq ATGGAAGTTGGATGAAAGTGGAGGTAAAGAGAAGACGTTTGA OR 0 re findall r ATG 9 TAA TAG TGA seq 首先让我解释一下我正在尝试做什么 如果这令人困惑
  • ValueError:数据必须为正(boxcox scipy)

    我正在尝试将我的数据集转换为正态分布 0 8 298511e 03 1 3 055319e 01 2 6 938647e 02 3 2 904091e 02 4 7 422441e 02 5 6 074046e 02 6 9 265747e
  • 正在使用 PIL 保存损坏的图像

    我遇到一个问题 操作图像像素导致保存损坏的图像 因此 我使用 PIL 打开图像 然后将其转换为 NumPy 数组 image Image open myimage png np image np asarray image 然后 我转置图像
  • 在 Linux 上使用多处理时,TKinter 窗口不会出现

    我想生成另一个进程来异步显示错误消息 同时应用程序的其余部分继续 我正在使用multiprocessingPython 2 6 中的模块来创建进程 我试图用以下命令显示窗口TKinter 这段代码在Windows上运行良好 但在Linux上
  • 使用 subprocess.Popen() 或 subprocess.check_call() 时程序卡住

    我想从 python 运行一个程序并找到它的内存使用情况 为此 我正在使用 l a out lt in txt gt out txt p subprocess Popen l shell False stdout subprocess PI
  • 高级描述熊猫

    有没有像 pandas 那样更高级的功能 通常我会继续这样 r pd DataFrame np random randn 1000 columns A r describe 我会得到一份很好的总结 就像这样 A count 1000 000
  • Python-验证我的文档 xls 中是否存在工作表

    我正在尝试在空闲时间设计一个小程序 加载 xls 文件 然后在要扫描的文档中选择一张纸 步骤1 用户导入 xls文件 导入程序后检查文件是否存在 我能做到的 第 2 步 我要求用户提供要分析的文档表 xls 的名称 这就是它停止的地方 该程
  • 为什么将模块级代码放入函数中然后调用该函数在Python中速度更快?

    在亚历克斯 马尔泰利的回应中使 Python 脚本面向对象 https stackoverflow com questions 1813117 making a python script object oriented 他提到在 Pyth
  • Seaborn 中没有线性拟合的散点图

    我想知道是否有办法关闭seaborn中的线性拟合lmplot或者是否有一个等效函数可以生成散点图 当然 我也可以使用 matplotlib 但是 我发现 seaborn 中的语法和美学非常吸引人 例如 我想绘制以下情节 import sea
  • 为什么 Collections.counter 这么慢?

    我正在尝试解决罗莎琳德的基本问题 即计算给定序列中的核苷酸 并在列表中返回结果 对于那些不熟悉生物信息学的人来说 它只是计算字符串中 4 个不同字符 A C G T 出现的次数 我期望collections Counter是最快的方法 首先
  • 从 wxPython 事件处理程序中调用函数

    我正在努力寻找一种在 wxPython 事件处理函数中使用函数的方法 假设我有一个按钮 单击该按钮时 它会使用事件处理程序运行一个名为 OnRun 的函数 但是 用户忘记单击 OnRun 按钮之前的 RadionButton 我想弹出一个
  • Python 相当于 Scala 案例类

    Python 中是否有与 Scala 的 Case Class 等效的东西 就像自动生成分配给字段而无需编写样板的构造函数一样 当前执行此操作的现代方法 从 Python 3 7 开始 是使用数据类 https www python org
  • 为boost python编译的.so找不到模块

    我正在尝试将 C 代码包装到 python 中 只需一个类即可导出两个函数 我编译为map so 当我尝试时import map得到像噪音一样的错误 Traceback most recent call last File
  • MoviePY 无法在 Windows 上检测 ImageMagick 二进制文件

    我刚买了一台新笔记本电脑 想要设置MoviePY在那新的Windows 64x Python3 7 0 机器 我对所有内容都进行了三次检查 但是当涉及到我的代码的文本部分时 它向我抛出了这个错误 OSError MoviePy Error

随机推荐

  • 客户管理系统 详细流程(不用三大框架)

    客户管理系统的详细编写流程 一 系统设计 1 需求分析 系统中包含哪些数据模型 数据模型存在怎样的关系 E R图 UML 用例图 2 制作页面Demo 和真实系统效果一样 给客户确认需求 3 技术选型 环境搭建 软件建模工具 IBM RSA
  • [Python-9]GUI编程

    一 GUI图形用户界面编程介绍 我们前面实现的都是基于控制台的程序 程序和用户的交互通过控制台来完成 本章 我们将学习GUI Graphics User Interface 即图形用户界面编程 我们可以通过python提供的丰富的组件 快速
  • SSM商城项目实战:后台管理用户认证

    SSM商城项目实战 后台管理用户认证 1 项目概述 本项目是一个基于SSM Spring SpringMVC MyBatis 框架的商城后台管理系统 用于管理商品 订单和用户等信息 在本篇博客中 我们将重点介绍如何实现后台管理用户的认证功能
  • opengl点的绘制

    include stdafx h include
  • unity的错误解决办法:NullReferenceException: Object reference not set to an instance of an object;tiny proje

    普通unity项目 正常的项目中 这是个非常初级的错误 比较常见的原因是就是在对象被引用前没有实例化 即是说 你要管理好程序的生命周期 解决办法1 设置不同的生命周期 生命周期就是脚本中的Awake Start Update等方法 他们会以
  • WIN 10环境下安装pytorch环境并跑通Yolox-demo

    文章目录 前言 一 Pytorch是什么 二 搭建Pytorch框架步骤 1 安装Anaconda 2 安装CUDA和CUDNN 3 安装Pytorch 三 Pycharm上跑通Yolox 1 下载Yolox项目 2 导入pycharm并设
  • 使用IDEA快速部署到Docker云端

    一 Docker对外提供服务 1 编辑服务配置文件 vim lib systemd system docker service 2 定位到ExecStart开头行 将原本的ExecStart usr bin dockerd H fd con
  • js aes加密_威feng网站的aes算法破解

    网站是 aHR0cHM6Ly93d3cuZmVuZy5jb20v 话说这个网站在过年前使用了aes算法 当然过年后也是aes 但就是把秘钥换了 换成更需要解密一段字符串 然后获得秘钥 最后请求时候再去用这个秘钥加密 并且最后发现秘钥和偏移是
  • mysql语句添加字段和注释

    简单点的 工作中需要对某个表进行添加字段 输出脚本 添加字段关键字 alter 注释关键字 comment ALTER TABLE 表名 ADD 字段名 varchar 20 COMMENT 注释内容 不要注释 去掉comment就行了 o
  • python opencv图片二值化后取出图片中心区域的轮廓

    python opencv图片二值化后取出图片中心区域的轮廓 1 导入必要的库 import cv2 import numpy as np 2 读取图片并将其转为灰度图像 image cv2 imread image jpg gray cv
  • vue封装指针样式

    新建style js文件 HandPointer 鼠标经过 BodyPointer 默认指针 可以设置多个样式 const HandPointer gt return cursor url require 图片路径 auto const B
  • Antd中RangePicker组件弹出的日历中英混杂,月份和周显示为英问题

    在开发中需要用到antd中的RangePicker组件 但是出现了一个问题 日历中英混杂 年份是中文 月份和周都是英文 搜索了一下这个问题主要是moment造成的 可能有很多种情况 需要根据项目的自身情况来修改 首先确定一下项目中packa
  • 计算机视觉基础(十)—— HOG特征描述算子之行人检测

    本次任务将学习一种在深度学习之前非常流行的图像特征提取技术 方向梯度直方图 Histogram of Oriented Gradients 简称HOG特征 HOG特征是在2005年CVPR的会议发表 在图像手工特征提取方面具有里程碑式的意义
  • JAVA基础:线程池的使用

    目录 1 概述 2 线程池的优势 2 1 线程池为什么使用自定义方式 2 2 封装的线程池工具类有什么好处 3 线程池的七大参数 3 线程池的创建 3 1 固定数量的线程池 3 2 带缓存的线程池 3 3 执 定时任务 3 4 定时任务单线
  • Linux查看tomcat安装路径

    查看tomcat安装路径 查看tomcat安装路径 sudo find name tomcat
  • python调用c++之pybind11

    之前一直从事c 相关算法及代码的相关工作 因公司内部代码管理需要 需将算法封装待python平台使用 根据此需求 对python调用c 代码的方式进行了学习 最终综合考虑封装难度及多代码管理使用pybind11进行了相关功能的实现 pybi
  • css实现图片和文字水平居中对齐

    方式一 vertical align middle 通过vertical align middle实现现图片与文字水平对齐 需要区分html是行内元素 还是块状元素 例如 标签img span是行内元素 标签p是块状元素则需要将标签p通过d
  • 【云原生之kubernetes实战】在k8s环境下部署Halo博客系统

    云原生之kubernetes实战 在k8s环境下部署Halo博客系统 一 Halo介绍 1 Halo简介 2 Halo特点 二 环境规划 1 本次实践环境规划 2 本次实践说明 三 环境检查 1 检查工作节点状态 2 检查系统pod状态 四
  • Github注册中,邮箱验证通不过解决办法

    使用谷歌注册的时候 验证收不到消息 1 注册出现如图问题 2 在注册失败的页面 点返回的箭头 点了之后不要动 3 然后进入开启一个新的页面 复制下面的链接 进去页面 可能有点慢 等一等 OctoCaptchahttps octocaptch
  • python爬虫的学习总结

    背景 基于django框架完成jira网页数据的爬取 由于对爬虫知识知道的太少 我开始了新的学习之旅 本文前半部分都是记录这一周主要的错误 如果想直接看最终成果 可以跳到本文 成功爬取 部分浏览 学习爬虫知识 在知道了本项目可能需要爬虫后