• [技术干货] Scrapy 框架入门简介【转】
    Scrapy 框架Scrapy是用纯Python实现一个为了爬取网站数据、提取结构性数据而编写的应用框架,用途非常广泛。框架的力量,用户只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页内容以及各种图片,非常之方便。Scrapy 使用了异步网络框架来处理网络通讯,可以加快我们的下载速度,不用自己去实现异步框架,并且包含了各种中间件接口,可以灵活的完成各种需求。Scrapy架构图(绿线是数据流向):Scrapy Engine(引擎): 负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯,信号、数据传递等。Scheduler(调度器): 它负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列,入队,当引擎需要时,交还给引擎。Downloader(下载器):负责下载Scrapy Engine(引擎)发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine(引擎),由引擎交给Spider来处理,Spider(爬虫):它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler(调度器),Item Pipeline(管道):它负责处理Spider中获取到的Item,并进行进行后期处理(详细分析、过滤、存储等)的地方.Downloader Middlewares(下载中间件):你可以当作是一个可以自定义扩展下载功能的组件。Spider Middlewares(Spider中间件):你可以理解为是一个可以自定扩展和操作引擎和Spider中间通信的功能组件(比如进入Spider的Responses;和从Spider出去的Requests)Scrapy的运作流程代码写好,程序开始运行...1 引擎:Hi!Spider, 你要处理哪一个网站?2 Spider:老大要我处理xxxx.com。3 引擎:你把第一个需要处理的URL给我吧。4 Spider:给你,第一个URL是xxxxxxx.com。5 引擎:Hi!调度器,我这有request请求你帮我排序入队一下。6 调度器:好的,正在处理你等一下。7 引擎:Hi!调度器,把你处理好的request请求给我。8 调度器:给你,这是我处理好的request9 引擎:Hi!下载器,你按照老大的下载中间件的设置帮我下载一下这个request请求10 下载器:好的!给你,这是下载好的东西。(如果失败:sorry,这个request下载失败了。然后引擎告诉调度器,这个request下载失败了,你记录一下,我们待会儿再下载)11 引擎:Hi!Spider,这是下载好的东西,并且已经按照老大的下载中间件处理过了,你自己处理一下(注意!这儿responses默认是交给def parse()这个函数处理的)12 Spider:(处理完毕数据之后对于需要跟进的URL),Hi!引擎,我这里有两个结果,这个是我需要跟进的URL,还有这个是我获取到的Item数据。13 引擎:Hi !管道 我这儿有个item你帮我处理一下!调度器!这是需要跟进URL你帮我处理下。然后从第四步开始循环,直到获取完老大需要全部信息。14 管道``调度器:好的,现在就做!注意!只有当调度器中不存在任何request了,整个程序才会停止,(也就是说,对于下载失败的URL,Scrapy也会重新下载。)制作 Scrapy 爬虫 一共需要4步:新建项目 (scrapy startproject xxx):新建一个新的爬虫项目明确目标 (编写items.py):明确你想要抓取的目标制作爬虫 (spiders/xxspider.py):制作爬虫开始爬取网页存储内容 (pipelines.py):设计管道存储爬取内容Scrapy的安装介绍Scrapy框架官方网址:http://doc.scrapy.org/en/latestScrapy中文维护站点:http://scrapy-chs.readthedocs...Windows 安装方式Python 2 / 3升级pip版本:pip install --upgrade pip通过pip 安装 Scrapy 框架pip install ScrapyUbuntu 需要9.10或以上版本安装方式Python 2 / 3安装非Python的依赖 sudo apt-get install python-dev python-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev通过pip 安装 Scrapy 框架 sudo pip install scrapy安装后,只要在命令终端输入 scrapy,提示类似以下结果,代表已经安装成功具体Scrapy安装流程参考:http://doc.scrapy.org/en/late... 里面有各个平台的安装方法入门案例学习目标创建一个Scrapy项目定义提取的结构化数据(Item)编写爬取网站的 Spider 并提取出结构化数据(Item)编写 Item Pipelines 来存储提取到的Item(即结构化数据)一. 新建项目(scrapy startproject)在开始爬取之前,必须创建一个新的Scrapy项目。进入自定义的项目目录中,运行下列命令:scrapy startproject mySpider其中, mySpider 为项目名称,可以看到将会创建一个 mySpider 文件夹,目录结构大致如下:下面来简单介绍一下各个主要文件的作用:scrapy.cfg :项目的配置文件mySpider/ :项目的Python模块,将会从这里引用代码mySpider/items.py :项目的目标文件mySpider/pipelines.py :项目的管道文件mySpider/settings.py :项目的设置文件mySpider/spiders/ :存储爬虫代码目录二、明确目标(mySpider/items.py)我们打算抓取:http://www.itcast.cn/channel/... 网站里的所有讲师的姓名、职称和个人信息。打开mySpider目录下的items.pyItem 定义结构化数据字段,用来保存爬取到的数据,有点像Python中的dict,但是提供了一些额外的保护减少错误。可以通过创建一个 scrapy.Item 类, 并且定义类型为scrapy.Field的类属性来定义一个Item(可以理解成类似于ORM的映射关系)。接下来,创建一个ItcastItem 类,和构建item模型(model)。import scrapyclass ItcastItem(scrapy.Item):name = scrapy.Field()title = scrapy.Field()info = scrapy.Field()三、制作爬虫 (spiders/itcastSpider.py)爬虫功能要分两步:1. 爬数据在当前目录下输入命令,将在mySpider/spider目录下创建一个名为itcast的爬虫,并指定爬取域的范围:scrapy genspider itcast "itcast.cn"打开 mySpider/spider目录里的 itcast.py,默认增加了下列代码:import scrapyclass ItcastSpider(scrapy.Spider): name = "itcast" allowed_domains = ["itcast.cn"] start_urls = ( 'http://www.itcast.cn/', ) def parse(self, response): pass其实也可以由我们自行创建itcast.py并编写上面的代码,只不过使用命令可以免去编写固定代码的麻烦要建立一个Spider, 你必须用scrapy.Spider类创建一个子类,并确定了三个强制的属性 和 一个方法。name = "" :这个爬虫的识别名称,必须是唯一的,在不同的爬虫必须定义不同的名字。allow_domains = [] 是搜索的域名范围,也就是爬虫的约束区域,规定爬虫只爬取这个域名下的网页,不存在的URL会被忽略。start_urls = () :爬取的URL元祖/列表。爬虫从这里开始抓取数据,所以,第一次下载的数据将会从这些urls开始。其他子URL将会从这些起始URL中继承性生成。parse(self, response) :解析的方法,每个初始URL完成下载后将被调用,调用的时候传入从每一个URL传回的Response对象来作为唯一参数,主要作用如下:负责解析返回的网页数据(response.body),提取结构化数据(生成item)生成需要下一页的URL请求。将start_urls的值修改为需要爬取的第一个urlstart_urls = ("http://www.itcast.cn/channel/teacher.shtml",)修改parse()方法def parse(self, response): filename = "teacher.html" open(filename, 'wb+').write(response.body)然后运行一下看看,在mySpider目录下执行:scrapy crawl itcast是的,就是 itcast,看上面代码,它是 ItcastSpider 类的 name 属性,也就是使用 scrapy genspider命令的唯一爬虫名。运行之后,如果打印的日志出现 [scrapy] INFO: Spider closed (finished),代表执行完成。 之后当前文件夹中就出现了一个 teacher.html 文件,里面就是我们刚刚要爬取的网页的全部源代码信息。**注意,Python2.x默认编码环境是ASCII,当和取回的数据编码格式不一致时,可能会造成乱码;我们可以指定保存内容的编码格式,一般情况下,我们可以在代码最上方添加:**import sysreload(sys)sys.setdefaultencoding("utf-8")这三行代码是Python2.x里解决中文编码的万能钥匙,经过这么多年的吐槽后Python3学乖了,默认编码是Unicode了...(祝大家早日拥抱Python3)2. 取数据爬取整个网页完毕,接下来的就是的取过程了,首先观察页面源码:<div class="li_txt"> <h3> xxx </h3> <h4> xxxxx </h4> <p> xxxxxxxx </p>是不是一目了然?直接上XPath开始提取数据吧。我们之前在mySpider/items.py 里定义了一个ItcastItem类。 这里引入进来 from mySpider.items import ItcastItem然后将我们得到的数据封装到一个 ItcastItem 对象中,可以保存每个老师的属性:from mySpider.items import ItcastItemdef parse(self, response): #open("teacher.html","wb").write(response.body).close() # 存放老师信息的集合 items = [] for each in response.xpath("//div[@class='li_txt']"): # 将我们得到的数据封装到一个 `ItcastItem` 对象 item = ItcastItem() #extract()方法返回的都是unicode字符串 name = each.xpath("h3/text()").extract() title = each.xpath("h4/text()").extract() info = each.xpath("p/text()").extract() #xpath返回的是包含一个元素的列表 item['name'] = name[0] item['title'] = title[0] item['info'] = info[0] items.append(item) # 直接返回最后数据 return items我们暂时先不处理管道,后面会详细介绍。保存数据scrapy保存信息的最简单的方法主要有四种,-o 输出指定格式的文件,,命令如下:json格式,默认为Unicode编码scrapy crawl itcast -o teachers.jsonjson lines格式,默认为Unicode编码scrapy crawl itcast -o teachers.jsonlcsv 逗号表达式,可用Excel打开scrapy crawl itcast -o teachers.csvxml格式scrapy crawl itcast -o teachers.xml思考如果将代码改成下面形式,结果完全一样。请思考 yield 在这里的作用:from mySpider.items import ItcastItemdef parse(self, response): #open("teacher.html","wb").write(response.body).close() # 存放老师信息的集合 #items = [] for each in response.xpath("//div[@class='li_txt']"): # 将我们得到的数据封装到一个 `ItcastItem` 对象 item = ItcastItem() #extract()方法返回的都是unicode字符串 name = each.xpath("h3/text()").extract() title = each.xpath("h4/text()").extract() info = each.xpath("p/text()").extract() #xpath返回的是包含一个元素的列表 item['name'] = name[0] item['title'] = title[0] item['info'] = info[0] #items.append(item) #将获取的数据交给pipelines yield item
  • [技术干货] 爬虫的基本原理
    前言随着互联网信息的爆炸式增长,如何从海量的数据中提取出有价值的信息成为了一个热门话题。爬虫(Crawler)作为一种自动化工具,可以帮助我们高效地从网络上抓取并解析数据。在本文中,我们将深入探索爬虫的基本原理,帮助读者理解并掌握这一强大的工具。一、爬虫的基本概念爬虫,也称为网络爬虫、网页爬虫,是一种按照一定规则自动抓取互联网信息的程序。爬虫可以从指定的网页出发,沿着链接抓取网页内容,将获取的数据存储到本地或数据库中,以便后续分析和利用。二、爬虫的基本原理爬虫的工作原理可以分为以下几个步骤:发送请求:爬虫首先通过HTTP或HTTPS协议向目标网页发送请求。这个请求包含了要获取的网页地址(URL)以及其他一些可选的参数,如请求头、请求体等。接收响应:目标网页在接收到请求后,会返回一个响应。这个响应包含了网页的内容,通常是以HTML、XML或其他格式表示的。响应中还包含了一些元数据,如状态码、响应头等。解析网页:爬虫在接收到响应后,需要对网页内容进行解析。这个过程主要是将网页中的结构化信息提取出来,如文本、链接、图片等。常用的解析工具有正则表达式、XPath、BeautifulSoup等。提取数据:爬虫根据解析结果,提取出需要的数据。这些数据可以是网页中的特定信息,如标题、摘要、正文等,也可以是网页之间的链接关系。存储数据:爬虫将提取到的数据存储到本地或数据库中,以便后续的分析和利用。三、爬虫的分类根据爬虫的用途和实现方式,我们可以将爬虫分为以下几类:通用爬虫:这类爬虫没有特定的目标网页,而是对整个互联网进行爬取。它们通常用于构建搜索引擎的索引库。聚焦爬虫:这类爬虫针对特定的主题或需求,只对与主题相关的网页进行爬取。它们通常用于获取特定领域的数据。增量爬虫:这类爬虫只爬取最近更新的网页内容,以便及时发现和获取新信息。它们通常用于新闻、博客等时效性较强的网站。四、爬虫的注意事项在使用爬虫时,我们需要注意以下几点:遵守法律法规:在进行爬虫开发和使用时,需要遵守相关法律法规,如《中华人民共和国网络安全法》等。尊重网站权益:爬虫在爬取网页时,需要尊重网站的权益,如遵守robots.txt协议,避免对网站造成过大的负担。数据处理与隐私保护:在处理和存储爬虫获取的数据时,需要注意数据的安全性和隐私保护,避免泄露敏感信息。总结爬虫作为一种强大的网络信息抓取工具,可以帮助我们高效地从互联网上获取有价值的信息。通过了解爬虫的基本原理和注意事项,我们可以更好地利用爬虫为我们的生活和工作提供便利。
  • [技术干货] 基于Python+django影片数据爬取与数据分析设计与实现-转载
     一、 前言介绍: 快速发展的社会中,人们的生活水平都在提高,生活节奏也在逐渐加快。为了节省时间和提高工作效率,越来越多的人选择利用互联网进行线上打理各种事务,通过线上管理影片数据爬取与数据分析也就相继涌现。与此同时,人们开始接受方便的生活方式,他们不仅希望页面简单大方,还希望操作方便,可以快速锁定他们需要的影片数据爬取与数据分析方式。基于这种情况,我们需要这样一个界面简单大方、功能齐全的系统来解决用户问题,满足用户需求。  图片请到原链接!  课题主要分为两大模块:即管理员模块和用户模块,主要功能包括系统首页、个人中心、用户管理、电影管理、系统管理等;  二 、功能设计: 影片数据爬取与数据分析分为两个部分,即管理员和用户。该系统是根据用户的实际需求开发的,贴近生活。从管理员处获得的指定账号和密码可用于进入系统和使用相关的系统应用程序。管理员拥有最大的权限,其次是用户。管理员一般负责整个系统的运行维护和总体协调。  系统结构如图所示。   图系统结构图  三、功能实现: 系统登录实现 系统登录,在登录页面选择需要登录的角色,在正确输入用户名和密码后,进入操作系统进行操作;系统登录界面如图5-1所示:   图5-1 系统登录界面  用户注册:在登录页面注册按钮,进入用户注册界面,输入用户信息点击注册进行用户注册操作,用户注册界面如图5-2所示:   图5-2 用户注册界面  管理员实现  管理员进入主页面,主要功能包括对系统首页、个人中心、用户管理、电影管理、系统管理等进行操作。管理员主界面如图5-3所示:   图5-3 管理员主界面  管理员点击用户管理。进入用户页面输入用户名可以查询,新增或删除用户列表,并根据需要对用户信息进行查看详情,修改或删除操作。如图5-4所示:   图5-4用户管理界面  管理员点击电影管理。进入电影页面可以查询,新增,删除或爬取数据电影列表,并根据需要对电影信息进行查看详情,修改或删除操作。如图5-5所示:   图5-5电影管理界面  管理员点击系统管理。进入系统简介页面输入标题可以查询系统简介列表,并根据需要对系统简介信息进行查看详情或修改操作。如图5-6所示:   图5-6系统管理界面  管理员点击跳转到看板。进入看板页面可以查看类型、评分、地区、语言、电影总数和导演等详细数据分析。如图5-7所示:   图5-7数据分析界面  用户模块实现  用户进入主页面,主要功能包括对系统首页、个人中心、电影管理等进行操作。用户主界面如图5-8所示:   图5-8 用户主界面  用户点击电影管理。进入电影页面可以查询或爬取数据电影列表,并根据需要对电影信息进行查看详情操作。如图5-9所示:   图5-9电影管理界面  四、库表设计: ​概念设计是将整体分为在地面上表达出来的单个个体。E-R图形象的连接了实体模型和概念模型。因此,E-R图需要根据数据库表和表字段进行合理设计,表达的概念知识点用图形描述,可以直观地让相应人员清楚,并分解整个E-R图[13]。我们通常表达不清晰没有概念的东西。但是通过E-R之间的联系,E-R模型法是对这种模糊概念的事务最简单、最常用的设计方法。    (1) 用户实体属性图如下图4-2所示。   图4-2用户实体属性图  (2) 电影实体属性图如下图所示。   图4-3电影实体属性图  (3) 系统简介实体属性图如下图4-4所示。   图系统简介实体属性图  五、关键代码: # coding:utf-8 from configparser import ConfigParser import logging, sys, os import pymysql   from util.configread import config_read     class Create(object):     def __init__(self, dbtype, host, port, user, passwd, dbName, charset):         self.dbtype, self.host, self.port, self.user, self.passwd, self.dbName, self.charset = dbtype, host, port, user, passwd, dbName, charset         self.conn = pymysql.connect(host=self.host, user=self.user, passwd=self.passwd, port=self.port,                                     charset=self.charset)         self.cur = self.conn.cursor()       def create_db(self, sql):         self.cur.execute(sql)         self.conn.commit()       def create_tables(self, sqls):         use_sql = '''use `{}`;'''.format(self.dbName)         self.cur.execute(use_sql)           for sql in sqls:             self.cur.execute(sql)             self.conn.commit()       def conn_close(self):         self.cur.close()         self.conn.close()  # 管道文件   import pymysql import pymssql from itemadapter import ItemAdapter   class SpiderPipeline(object):       # 打开数据库     def open_spider(self, spider):         type = spider.settings.get('TYPE', 'mysql')         host = spider.settings.get('HOST', 'localhost')         port = int(spider.settings.get('PORT', 3306))         user = spider.settings.get('USER', 'root')         password = spider.settings.get('PASSWORD', '123456')           try:             database = spider.databaseName         except:             database = spider.settings.get('DATABASE', '')           if type == 'mysql':             self.connect = pymysql.connect(host=host, port=port, db=database, user=user, passwd=password, charset='utf8')         else:             self.connect = pymssql.connect(host=host, user=user, password=password, database=database)                      self.cursor = self.connect.cursor()       # 关闭数据库     def close_spider(self, spider):         self.connect.close()       # 对数据进行处理     def process_item(self, item, spider):         self.insert_db(item, spider.name)         return item       # 插入数据     def insert_db(self, item, spiderName):         values = tuple(item.values())         # print(values)           qmarks = ', '.join(['%s'] * len(item))         cols = ', '.join(item.keys())           sql = "INSERT INTO %s (%s) VALUES (%s)" % (spiderName.replace('Spider', ''), cols, qmarks)           self.cursor.execute(sql, values)         self.connect.commit() ———————————————— 版权声明:本文为CSDN博主「java李杨勇」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_39709134/article/details/134658464 
  • [技术干货] 深入了解百度爬虫工作原理-转载
     讲在前面 什么是爬虫 在当今信息爆炸的时代,互联网成为了人们获取各种信息的主要途径。然而,互联网上的信息分布在各种网站和页面之中,要手动访问和收集这些信息无疑是一项耗时且繁琐的任务。为了解决这个问题,爬虫技术应运而生。   爬虫的定义 爬虫(Crawler),又称网络蜘蛛(Spider)或网络机器人(Bot),是一种自动化程序,用于在互联网上获取和抓取信息。 爬虫通过模拟浏览器的行为,自动访问网页并提取所需的数据。它可以遍历整个互联网,从而实现大规模的数据采集和处理。 爬虫的作用 数据采集与分析:爬虫可以帮助人们从互联网上获取大量的数据,用于分析、研究和决策。 搜索引擎索引:搜索引擎通过爬虫抓取网页内容,建立索引,以便用户可以快速找到他们需要的信息。 网络监测与安全:爬虫可以用于监测网络上的信息、追踪恶意行为并提供安全保护。 数据挖掘与推荐:通过分析爬虫获取的数据,可以进行数据挖掘和个性化推荐等应用。 爬虫的种类 通用爬虫:用于整个互联网的信息抓取,如搜索引擎的爬虫。 垂直爬虫:针对特定领域或网站的信息抓取,例如新闻网站、电商网站等。 增量式爬虫:仅抓取更新的页面或有修改的页面,以提高效率和节省资源。 百度爬虫的作用 百度爬虫作为百度搜索引擎的核心组成部分,在整个搜索服务过程中扮演着重要的角色。它的主要作用是收集、索引和更新互联网上的网页信息,以便用户能够通过输入关键词获取相关的搜索结果。  百度爬虫通过不断抓取互联网上的网页,并将这些网页存储在巨大的索引库中。这个过程是一个持续进行的任务,以确保索引库中的数据能够及时更新。通过广泛收集网页信息,百度爬虫为搜索引擎提供了丰富的搜索资源,使得用户能够找到他们所需的相关内容。  网页抓取:百度爬虫通过自动化程序扫描互联网上的网页,发现新的网页并抓取其中的内容。这样可以确保搜索引擎的索引库中包含最新的网页信息。 数据解析:百度爬虫对抓取的网页进行解析,提取其中的文本、图片、链接等信息。通过解析网页,百度爬虫能够理解网页的结构和内容,为后续的索引和检索做准备。 网页索引:百度爬虫将抓取和解析得到的网页数据存储在索引库中。索引库是一个巨大的数据库,其中存储了大量网页的关键词、标题、摘要等信息,以及指向每个网页的链接。 网页排名:百度爬虫通过分析网页的质量、相关度和用户反馈等因素,为每个网页赋予一个权重值。这个权重值在搜索结果中决定了网页的排名位置,从而影响了用户在搜索时所看到的结果顺序。 爬虫的基本原理 URL抓取与调度 URL抓取与调度是百度爬虫工作原理中的关键步骤,它负责确定哪些网页需要被爬取,并按照一定的规则和策略进行调度和管理。  百度爬虫开始抓取工作时,需要先确定一些起始的种子URL。这些种子URL可以是事先设定的一些重要网站,也可以是用户输入的搜索关键词。百度爬虫会从这些种子URL开始,逐步扩展到其他相关网页。  百度爬虫使用一个URL队列来存储待抓取的网页链接。在抓取过程中,百度爬虫会不断从队列中取出URL进行处理。当一个URL被抓取后,它所包含的链接也会被添加到队列中,以便进一步抓取。  为了避免重复抓取相同的网页,百度爬虫需要进行URL去重操作。这通常通过对已经抓取的URL和队列中的URL进行比较,排除重复的链接。URL去重是保证爬虫效率和准确性的重要步骤。  为了提高抓取效率,百度爬虫还会采用一些策略,如多线程抓取、增量抓取和并行抓取等。这些策略可以帮助爬虫同时处理多个URL,并在保证抓取质量的前提下提高抓取速度。  页面下载与解析 页面下载 页面下载是指从互联网上获取网页内容并保存至本地存储设备的过程。百度爬虫首先根据抓取策略选择需要下载的网页,在下载过程中,爬虫会模拟浏览器的行为发送HTTP请求到目标网站的服务器,获取网页的响应。通过网络协议,爬虫将网页内容下载到本地,并进行存储以供后续处理。  页面解析 页面解析是指对下载的网页内容进行解析,提取出其中的有用信息。百度爬虫使用解析技术来理解网页的结构和内容,以便进行后续的索引和检索。解析过程包括以下几个步骤:  HTML解析:百度爬虫使用HTML解析器对网页的HTML代码进行解析,识别出网页的各个元素,如标题、正文、链接等。 文本提取:通过解析HTML结构,爬虫可以提取出网页中的文本内容,包括段落、标题、标签等。文本提取是后续建立索引和进行关键词匹配的基础。 链接提取:爬虫还会解析网页中的链接,提取出其他页面的URL,以便进行进一步的抓取。通过链接提取,爬虫可以不断扩展抓取范围,建立更全面的索引。 页面下载与解析是百度爬虫工作过程中必不可少的环节。通过下载网页并解析其中的内容,百度爬虫能够获取网页的信息,并进一步进行索引、排名等操作,为用户提供准确、有价值的搜索结果。  数据存储与索引 在百度爬虫的工作中,数据存储与索引是非常重要的环节,通过高效的数据存储和索引机制来管理大规模的网页数据。   数据存储 数据存储是指将从互联网上下载的网页数据保存在合适的存储设备中。百度爬虫需要处理大量的网页数据,因此需要一个高效的存储系统来存储这些数据。通常,百度爬虫使用分布式存储系统,将数据分布在多个节点上,以提高存储容量和读写性能。  索引建立 索引建立是指将抓取到的网页数据进行整理和组织,以便用户进行快速检索。百度爬虫通过建立索引,将网页的关键信息以及对应的URL进行记录和归类。索引的建立可以分为以下几个步骤:  关键词提取:百度爬虫会对网页的文本内容进行分词和提取关键词。这样可以建立一个关键词库,记录每个关键词出现的频率和位置。 倒排索引:倒排索引是一种常用的索引结构,它将关键词作为索引的键,将对应的网页URL列表作为值。这样,用户在搜索时只需查询关键词,就可以快速找到相关的网页。 索引更新:由于互联网上的网页内容是动态变化的,爬虫需要及时更新索引。当新的网页被抓取并解析后,爬虫会将其加入到索引库中,保证索引的及时性和准确性。 百度爬虫的工作流程 种子URL的选择 在选择种子URL时,百度爬虫通常会结合多种策略来提高抓取的效果和覆盖范围。同时,为了保证抓取的合法性和合规性,百度爬虫会遵守相关的抓取规则和政策,避免抓取禁止访问或敏感内容的网页。  种子URL是指作为起始点的初始网页URL。它们是百度爬虫开始抓取过程的入口点,决定了抓取的起始位置和范围。选择合适的种子URL对于爬虫的效率和抓取结果都有重要影响。  百度爬虫选择种子URL的策略可以根据不同的需求和场景进行调整,常见的策略有:  首页链接:选择网站的首页链接作为种子URL是一种常见的策略。首页通常包含了网站的核心内容和导航链接,抓取首页可以较全面地覆盖网站的主要信息。 热门页面:选择网站上热门的、受欢迎的页面作为种子URL也是一种常用策略。这些页面通常包含了重要的内容和高质量的链接,抓取这些页面可以提高爬虫的抓取效果。 主题相关页面:根据用户指定的主题或关键词,选择与之相关的页面作为种子URL。这样可以更加精准地抓取与特定主题相关的网页。 历史数据:在一些情况下,可以选择已有的历史数据中的URL作为种子URL。这些URL可能是之前抓取过的、已经验证有效的网页,可以作为起始点进行新一轮的抓取。 抓取与解析页面 在百度爬虫的工作中,抓取和解析页面是核心环节之一。通过抓取和解析页面,百度爬虫能够获取目标网页的内容,并从中提取有用的信息。这些信息可以用于建立索引、计算网页的权重和相关性等,为用户提供准确和有用的搜索结果。   页面抓取 页面抓取是指通过网络请求获取目标网页的过程。百度爬虫会按照事先设定的规则和策略,通过HTTP或HTTPS协议向目标网址发送请求,获取网页的源代码。常见的页面抓取方式包括使用HTTP库发送GET或POST请求、模拟浏览器行为进行爬取等。  页面解析 页面解析是将抓取到的网页源代码进行处理和提取有用信息的过程。百度爬虫需要从网页中提取出关键信息,例如标题、正文内容、链接等。常见的页面解析方式包括:  正则表达式:使用正则表达式匹配和提取特定的文本模式。例如,使用正则表达式提取HTML标签中的内容。 XPath:使用XPath语法进行HTML/XML文档的解析。XPath通过路径表达式定位和选择节点,可以方便地提取所需数据。 CSS选择器:使用CSS选择器语法进行HTML文档的解析。通过选择器选择特定的HTML元素,提取相应的数据。 数据处理 在抓取和解析页面的过程中,百度爬虫还需要进行一些数据处理的步骤。例如:  数据清洗:对抓取到的数据进行清洗和格式化,去除不必要的标签、空格或特殊字符。 数据存储:将解析得到的数据保存到合适的格式中,例如文本文件、数据库或分布式存储系统。 错误处理:处理抓取过程中可能出现的错误,例如网络连接失败、页面不存在等情况。 抓取策略与规则 Robots.txt协议 在百度爬虫的工作中,Robots.txt协议扮演着重要的角色。   作用 Robots.txt是一种位于网站根目录下的文本文件,用于指导搜索引擎爬虫访问网站时应该遵守的规则。通过Robots.txt文件,网站管理员可以告诉搜索引擎爬虫哪些页面可以抓取,哪些页面不应该被抓取,以及抓取频率的限制等信息。Robots.txt协议有效地帮助网站管理者控制搜索引擎对其网站内容的抓取和索引行为。  实际应用 在百度爬虫的工作中,会遵循网站的Robots.txt文件中的规则来确定哪些页面可以抓取,哪些页面不应该被抓取。百度爬虫会定期访问网站的Robots.txt文件,并根据其中的规则来调整抓取的行为,以确保遵守网站所有者的指示。  Robots.txt规则 Robots.txt文件包含了一些常用的指令和规则,例如:  User-agent: 指定了该规则适用的搜索引擎爬虫代理,比如"*"表示适用于所有爬虫,"Baiduspider"表示只适用于百度爬虫。 Disallow: 指定了不允许抓取的URL路径,例如"/admin/"表示不允许抓取网站的管理员页面。 Allow: 指定了允许抓取的URL路径,优先级高于Disallow规则。 Crawl-delay: 指定了爬虫抓取的延迟时间,用于控制爬虫的访问频率。 网页质量评估 在百度搜索引擎中,网页质量评估用于确定哪些网页应该排名更高,确定其在搜索结果中的排名和展示优先级。  内容质量是网页质量评估的核心指标之一。百度搜索引擎会评估网页的内容是否原创、丰富、有用,并与搜索用户的查询意图匹配。  用户体验也是网页质量评估的重要参考因素。百度搜索引擎会考虑网页的加载速度、页面布局、广告数量等因素,以评估用户在访问该网页时的体验质量。以下是一些用户体验评估的关键因素:  链接质量也是网页质量评估的重要考虑因素之一。百度搜索引擎会评估网页的链接质量,包括外部链接和内部链接。  在网页质量评估中,百度搜索引擎还会考虑网页的信任度和安全性。  反爬虫机制  在互联网信息爬取的过程中,网站所有者可能会采取一些反爬虫机制来限制搜索引擎爬虫和其他自动化程序对其网站内容的访问。  IP限制与封锁 网站可能会对频繁访问的IP地址进行限制或封锁,以防止爬虫程序对网站进行大规模的数据抓取。  用户行为分析 一些网站会通过分析用户的访问行为来识别是否为爬虫程序的访问,如访问频率、点击模式等。  图像验证码 一些网站在特定情况下可能会强制要求用户输入图像验证码,以确认访问者是人类而非爬虫程序。  数据加载方式 一些网站可能会使用JavaScript等技术来动态加载页面内容, ers可能会对这种页面结构难以处理。 ———————————————— 版权声明:本文为CSDN博主「摔跤猫子」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_42794881/article/details/134462622 
  • [技术干货] 爬虫 python requests包的使用
    request简介requests 是一个常用的 HTTP 请求库,可以方便地向网站发送 HTTP 请求,并获取响应结果。 response.text 和response.content的区别 - response.text    - 类型:str    - 解码类型: requests模块自动根据HTTP 头部对响应的编码作出有根据的推测,推测的文本编码  - response.content    - 类型:bytes    - 解码类型: 没有指定 response响应对象的其它常用属性或方法 response = requests.get(url)中response是发送请求获取的响应对象;response响应对象中除了text、content获取响应内容以外还有其它常用的属性或方法:  response.url响应的url;有时候响应的url和请求的url并不一致 response.status_code 响应状态码 response.request.headers 响应对应的请求头 response.headers 响应头 response.request._cookies 响应对应请求的cookie;返回cookieJar类型 response.cookies 响应的cookie(经过了set-cookie动作;返回cookieJar类型 response.json()自动将json字符串类型的响应内容转换为python对象(dict or list) 发送带参数的请求 我们在使用百度搜索的时候经常发现url地址中会有一个 ?,那么该问号后边的就是请求参数,又叫做查询字符串  在url携带参数  直接对含有参数的url发起请求  ```python import requests   headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}   url = 'https://www.baidu.com/s?wd=python'   response = requests.get(url, headers=headers)  ```  通过params携带参数字典  ​    1.构建请求参数字典   ​    2.向接口发送请求的时候带上参数字典,参数字典设置给params  ```python import requests   headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}  这是目标urlurl = 'https://www.baidu.com/s?wd=python' 最后有没有问号结果都一样url = 'https://www.baidu.com/s?'  请求参数是一个字典 即wd=pythonkw = {'wd': 'python'}  带上请求参数发起请求,获取响应response = requests.get(url, headers=headers, params=kw)   print(response.content) ```   ------ cookieJar对象转换为cookies字典的方法 使用requests获取的resposne对象,具有cookies属性。该属性值是一个cookieJar类型,包含了对方服务器设置在本地的cookie。我们如何将其转换为cookies字典呢?  转换方法      cookies_dict = requests.utils.dict_from_cookiejar(response.cookies)   2. 其中response.cookies返回的就是cookieJar类型的对象 requests.utils.dict_from_cookiejar函数返回cookies字典   ---- 超时参数timeout的使用果。 在爬虫中,一个请求很久没有结果,就会让整个项目的效率变得非常低,这个时候我们就需要对请求进行强制要求,让他必须在特定的时间内返回结果,否则就报错。  超时参数timeout的使用方法      response = requests.get(url, timeout=3)   2. timeout=3表示:发送请求后,3秒钟内返回响应,否则就抛出异常  ```python import requests  url = 'https://twitter.com' response = requests.get(url, timeout=3)     # 设置超时时间  ```   ---- 了解代理以及proxy代理参数的使用 proxy代理参数通过指定代理ip,让代理ip对应的正向代理服务器转发我们发送的请求,那么我们首先来了解一下代理ip以及代理服务器  理解使用代理的过程 代理ip是一个ip,指向的是一个代理服务器 代理服务器能够帮我们向目标服务器转发请求  正向代理和反向代理的区别 前边提到proxy参数指定的代理ip指向的是正向的代理服务器,那么相应的就有反向服务器;现在来了解一下正向代理服务器和反向代理服务器的区别  从发送请求的一方的角度,来区分正向或反向代理 为浏览器或客户端(发送请求的一方)转发请求的,叫做正向代理    - 浏览器知道最终处理请求的服务器的真实ip地址,例如VPN 不为浏览器或客户端(发送请求的一方)转发请求、而是为最终处理请求的服务器转发请求的,叫做反向代理    - 浏览器不知道服务器的真实地址,例如nginx  代理ip(代理服务器)的分类 根据代理ip的匿名程度,代理IP可以分为下面三类:      - 透明代理(Transparent Proxy):透明代理虽然可以直接“隐藏”你的IP地址,但是还是可以查到你是谁。目标服务器接收到的请求头如下:        ```      REMOTE_ADDR = Proxy IP      HTTP_VIA = Proxy IP      HTTP_X_FORWARDED_FOR = Your IP      ```      - 匿名代理(Anonymous Proxy):使用匿名代理,别人只能知道你用了代理,无法知道你是谁。目标服务器接收到的请求头如下:        ```      REMOTE_ADDR = proxy IP      HTTP_VIA = proxy IP      HTTP_X_FORWARDED_FOR = proxy IP      ```      - 高匿代理(Elite proxy或High Anonymity Proxy):高匿代理让别人根本无法发现你是在用代理,所以是最好的选择。毫无疑问使用高匿代理效果最好。目标服务器接收到的请求头如下:        ```      REMOTE_ADDR = Proxy IP      HTTP_VIA = not determined      HTTP_X_FORWARDED_FOR = not determined      ```   2. 根据网站所使用的协议不同,需要使用相应协议的代理服务。从代理服务请求使用的协议可以分为:    - http代理:目标url为http协议    - https代理:目标url为https协议    - socks隧道代理(例如socks5代理)等:      1. socks 代理只是简单地传递数据包,不关心是何种应用协议(FTP、HTTP和HTTPS等)。      2. socks 代理比http、https代理耗时少。      3. socks 代理可以转发http和https的请求  proxies代理参数的使用 为了让服务器以为不是同一个客户端在请求;为了防止频繁向一个域名发送请求被封ip,所以我们需要使用代理ip;那么我们接下来要学习requests模块是如何使用代理ip的  用法:     ```python   response = requests.get(url, proxies=proxies)   ```  proxies的形式:字典 例如:     ```python   proxies = {        "http": "http://12.34.56.79:9527",        "https": "https://12.34.56.79:9527",    }   ```  注意:如果proxies字典中包含有多个键值对,发送请求时将按照url地址的协议来选择使用相应的代理ip  使用verify参数忽略CA证书 在使用浏览器上网的时候,有时能够看到下面的提示、: 原因:该网站的CA证书没有经过【受信任的根证书颁发机构】的认证 **关于CA证书以及受信任的根证书颁发机构点击了解更多**,课上我们不做展开  运行代码查看代码中向不安全的链接发起请求的效果 运行下面的代码将会抛出包含ssl.CertificateError ...字样的异常  ```python import requests url = "https://sam.huat.edu.cn:8443/selfservice/" response = requests.get(url) ```  解决方案 为了在代码中能够正常的请求,我们使用verify=False参数,此时requests模块发送请求将不做CA证书的验证:verify参数能够忽略CA证书的认证  ```python import requests url = "https://sam.huat.edu.cn:8443/selfservice/"  response = requests.get(url,verify=False) ``` requests模块发送post请求 登录注册( 在web工程师看来POST 比 GET 更安全,url地址中不会暴露用户的账号密码等信息) 需要传输大文本内容的时候( POST 请求对数据长度没有要求)  所以同样的,我们的爬虫也需要在这两个地方回去模拟浏览器发送post请求  代码实现 了解requests模块发送post请求的方法,百度翻译之后,完成代码  ```python import requests import json   1.获取请求的 URLpost_url='https://fanyi.baidu.com/sug' 并进行UA伪装header={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'}   2.参数处理(发起请求的URL有参数才需要处理),然后发送请求word=input('enter the word:') data={'kw':word} response=requests.post(post_url,data,headers=header)   3.获取响应数据.text返回的是一个字符串形式的 json 串,若确认响应数据是json类型,才可以使用.json返回,响应头信息当中的Content-Type可以确认dic_obj=response.json() print(type(response)) print(dic_obj) print(type(dic_obj))   # 4.持久化存储fp=open(word+'.json','w',encoding='utf-8')json.dump(dic_obj,fp=fp,ensure_ascii=False) # 中文不能使用Ascii进行编码``` 
  • [技术干货] java面试题基础篇 温故而知新 没事在地铁上多看看
    答案在路上,自由在风里,风吹哪页读哪页,哪页不懂撕哪页就是你,你太棒啦今天也要开开心心呀Java语言有哪些特点Java是一种高级编程语言,具有以下特点:面向对象:Java是一种纯面向对象的语言,所有代码都必须定义在类中。这使得Java非常适合开发大型应用程序和企业级软件。可移植性:Java的跨平台特性使其可以在不同的操作系统上运行,例如Windows、Linux和Mac OS等。简单易学:Java的语法相对简单,易于学习和使用。它还提供了大量的文档和教程,使初学者能够快速入门。高性能:Java的虚拟机(JVM)可以动态地将Java字节码翻译为本地机器代码,从而提高了程序的性能。安全性:Java提供了强大的安全功能,例如沙箱环境和安全管理器,可以帮助保护应用程序免受恶意代码的攻击。大量的类库:Java拥有丰富的类库,涵盖了各种领域,例如网络编程、图形用户界面、数据库访问等。开放源代码:Java是开源的,任何人都可以查看和修改其源代码,这使得Java社区可以不断改进和扩展其功能。面向对象和面向过程的区别面向对象(Object-Oriented,简称 OO)和面向过程(Procedural,简称 PO)是两种不同的编程范式。它们的主要区别在于关注点、设计原则和代码组织方式。以下是面向对象和面向过程之间的一些主要区别:关注点:面向对象编程关注数据和行为之间的关系,强调封装、继承和多态性。面向过程编程关注解决问题的方法,强调输入、处理和输出。设计原则:面向对象编程的设计原则包括封装、继承和多态性。封装确保数据和方法在类内部隐藏,外部无法直接访问;继承允许子类从父类继承属性和方法;多态性允许不同类的对象对同一消息作出不同的响应。面向过程编程的设计原则包括模块化、结构化和自顶向下的分解。模块化使得程序更容易理解和维护;结构化编程有助于提高代码的可读性和可维护性;自顶向下的分解将问题分解为更小的、易于管理的部分。代码组织方式:面向对象编程通常使用类和对象来表示实体和关系,通过调用方法实现功能。这种方式使得代码更加模块化、可重用和易于维护。面向过程编程通常使用函数和过程来表示任务,通过调用函数实现功能。这种方式使得代码更加结构化、易于理解和调试。面向对象编程和面向过程编程的主要区别在于关注点、设计原则和代码组织方式。面向对象编程强调数据和行为之间的关系,以及封装、继承和多态性等设计原则;而面向过程编程关注解决问题的方法,以及模块化、结构化和自顶向下的分解等设计原则。在实际开发中,选择合适的编程范式取决于项目需求、团队技能和其他因素八种基本数据类型的大小,以及他们的封装类Java八种基本数据类型的大小如下:byte:8位,取值范围为-128~127short:16位,取值范围为-32768~32767int:32位,取值范围为-2^31~2^31-1long:64位,取值范围为-2^63~2^63-1float:32位,取值范围为1.4E-45~3.4028235E+38double:64位,取值范围为4.9E-324~1.7976931348623157E+308char:16位,Unicode编码,一个字符占用两个字节,取值范围为'\u0000'~'\uffff'Java中对应八种基本数据类型的封装类分别为:Byte:java.lang.ByteShort:java.lang.ShortInteger:java.lang.IntegerLong:java.lang.LongFloat:java.lang.FloatDouble:java.lang.DoubleCharacter:java.lang.CharacterString:java.lang.String// 使用byte类型存储整数 byte b = 10; // b的值为10,占用一个字节(8位) System.out.println("b的值为" + b); // 输出结果为b的值为10 // 使用short类型存储整数 short s = 10; // s的值为10,占用两个字节(16位) System.out.println("s的值为" + s); // 输出结果为s的值为10 // 使用int类型存储整数 int i = 10; // i的值为10,占用四个字节(32位) System.out.println("i的值为" + i); // 输出结果为i的值为10 // 使用long类型存储整数 long l = 10L; // l的值为10,占用八个字节(64位) System.out.println("l的值为" + l); // 输出结果为l的值为10 // 使用float类型存储浮点数 float f = 10F; // f的值为10,占用四个字节(32位),其中小数部分占用了三个字节(24位) System.out.println("f的值为" + f); // 输出结果为f的值为10.000000 // 使用double类型存储浮点数 double d = 10D; // d的值为10,占用八个字节(64位),其中小数部分占用了七个字节(56位) System.out.println("d的值为" + d); // 输出结果为d的值为10.0000000000000000000000000000000000000000000000000​标识符的命名规则Java标识符的命名规则如下:标识符由字母、数字和下划线组成,但是必须以字母或下划线开头。Java中的关键字不能作为标识符的一部分,包括public、private、protected、static、interface、abstract、final、volatile、transient等关键字。标识符只能在类、接口、方法和变量中使用。Java中的保留字(如if、else、for、while等)也不能作为标识符的一部分。public class MyClass { private int myInt; // 整型变量myInt的命名符合Java标识符的命名规则 private String myString = "Hello World"; // 字符串变量myString的命名符合Java标识符的命名规则 public void myMethod() { double myDouble = 3.14; // 双精度浮点型变量myDouble的命名符合Java标识符的命名规则 } }​instanceof 关键字的作用instanceof 是 Java 中的一种关键字,用于检查一个对象是否是某个特定类(或其子类)的实例。它通常与 new 操作符一起使用,以创建一个新对象并检查其类型。public class Animal { public void makeSound() { System.out.println("Animal is making a sound."); } } public class Dog extends Animal { @Override public void makeSound() { System.out.println("Dog is barking."); } } public class Main { public static void main(String[] args) { Animal animal = new Dog(); // 创建一个狗对象 if (animal instanceof Dog) { // 检查动物对象是否是狗的实例 System.out.println("The animal is a dog."); } else if (animal instanceof Animal) { // 检查动物对象是否是任何动物的实例(包括狗和猫等) System.out.println("The animal is an animal."); } else { // 如果不是狗或任何动物的实例,则输出错误信息 System.out.println("The animal is neither a dog nor an animal."); } } }​Java自动装箱与拆箱Java自动装箱与拆箱是Java语言中的一个重要特性,它允许程序员在不需要显式声明类型的情况下将基本数据类型转换为对象类型。以下是Java自动装箱与拆箱的示例代码: 自动装箱 int a = 5; Integer b = a; // 将int类型的a自动装箱成Integer类型 System.out.println(b); // 输出结果为5 自动拆箱 Integer c = new Integer(5); int d = c; // 将Integer类型c自动拆箱成int类型 System.out.println(d); // 输出结果为5 抛出异常 int e = 0; try { int f = (int) "0"; // 将字符串"0"强制转换为int类型,会抛出NumberFormatException异常 } catch (NumberFormatException ex) { System.out.println(ex.getMessage()); // 输出结果为"Value is not a number" } 最大值和最小值检查 int g = Integer.MAX_VALUE; if (g == 10) { System.out.println("g is equal to 10"); // 输出结果为"g is equal to 10" } else { System.out.println("g is not equal to 10"); // 输出结果为"g is not equal to 10"​重载和重写的区别Java中的方法重载(Overloading)和方法重写(Overriding)是面向对象编程中的两个重要概念,它们在功能上有所区别。重载(Overloading): 重载是指在一个类中,方法名相同但参数列表不同的多个方法。编译器根据传递给方法的参数类型和数量来决定调用哪个方法。例如:public class MyClass { public void method(int a, int b) { System.out.println("Method with two integers: " + a + b); } public void method(String str) { System.out.println("Method with a string: " + str); } }​在这个例子中,我们定义了两个名为method的方法,它们具有相同的名称,但参数列表不同。当我们使用这两个方法时,编译器会根据传递给它的参数类型和数量自动选择合适的方法。重写(Overriding): 重写是指子类重新定义与父类同名、同参数列表和返回类型的方法。当子类重写一个父类的方法时,子类的方法将覆盖父类的方法。class Animal { public void eat() { System.out.println("Animal is eating"); } } class Dog extends Animal { @Override public void eat() { System.out.println("Dog is eating"); } } ​在这个例子中,我们定义了一个名为eat的方法,它是一个抽象方法,表示动物吃东西的行为。然后我们创建了一个名为Dog的子类,并重写了eat方法。当我们创建一个Dog对象并调用eat方法时,输出结果将是"Dog is eating",而不是"Animal is eating"。这是因为子类重写了父类的方法​​equals与==的区别Java中的equals()和==都是用于比较两个对象是否相等的方法,但它们之间有一些细微的差别。equals()方法是Object类中的方法,而==运算符是基本类型(如int、float等)的方法。因此,如果要比较一个自定义对象与另一个自定义对象,应该使用equals()方法而不是==运算符。equals()方法比较的是两个对象的内容是否相等,包括数据类型和值。而==运算符比较的是两个对象的引用是否相等,即它们是否指向同一个内存地址。​public class Person { private String name; private int age; // 构造函数 public Person(String name, int age) { this.name = name; this.age = age; } // equals方法 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && Objects.equals(name, person.name); } // toString方法 @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }在上面的代码中,我们定义了一个Person类,它有两个属性:name和age。我们在类中重写了equals()方法和toString()方法。在equals()方法中,我们首先判断两个对象是否为同一个对象,如果是则返回true,否则通过getClass()方法获取两个对象的类类型并进行比较,最后再比较两个对象的属性值是否相等。在toString()方法中,我们只是简单地将属性值拼接成字符串输出。Hashcode的作用Hashcode是Java中用于比较两个对象是否相等的方法。它的作用是返回一个整数,表示当前对象的哈希码值。如果两个对象的哈希码值相同,则说明它们在内存中的地址相同,即相等。public class HashCodeExample { public static void main(String[] args) { String str1 = "Hello"; String str2 = "World"; int hash1 = str1.hashCode(); // 计算str1的哈希码值 int hash2 = str2.hashCode(); // 计算str2的哈希码值 System.out.println("str1的哈希码值为:" + hash1); System.out.println("str2的哈希码值为:" + hash2); if (hash1 == hash2) { // 判断两个字符串的哈希码值是否相等 System.out.println("str1和str2相等"); } else { System.out.println("str1和str2不相等"); } } } str1的哈希码值为:504839640 str2的哈希码值为:720774163 str1和str2不相等​​String、String StringBuffffer 和 StringBuilder 的区别是什么?Java中的String、StringBuffer和StringBuilder都是用于处理字符串的类,但它们之间存在一些关键区别。以下是关于这三个类的详细说明:String:String类是Java中最常用的字符串类,它表示一个不可变(immutable)的字符序列。当你创建一个String对象时,Java会在堆内存中分配一块连续的空间来存储这个字符串。这意味着一旦你创建了一个String对象,你就无法更改其内容。String str1 = "Hello, world!"; System.out.println(str1); // 输出: Hello, world!​StringBuffer:StringBuffer类是一个可变的字符串缓冲区,允许你在运行时修改字符串。当你创建一个StringBuffer对象时,Java会分配一块动态增长的内存空间来存储这个字符串。这意味着你可以在程序运行过程中随时修改字符串的内容。StringBuffer strBuffer = new StringBuffer("Hello, Java!"); strBuffer.append(", World!"); // 修改字符串内容 System.out.println(strBuffer); // 输出: Hello, Java!, World!StringBuilder:StringBuilder类也是一个可变的字符串缓冲区,但它的行为与StringBuffer类似,但性能更好。当你创建一个StringBuilder对象时,Java同样会分配一块动态增长的内存空间来存储这个字符串。然而,由于它的内部实现方式不同,它在修改字符串时的速度通常比StringBuffer快。StringBuilder stringBuilder = new StringBuilder("Hello, Java!"); stringBuilder.append(", World!"); // 修改字符串内容 System.out.println(stringBuilder); // 输出: Hello, Java!, World!如果你需要在程序运行过程中频繁地修改字符串,那么使用StringBuilder或StringBuffer可能更合适。而如果你只需要创建一个不可变的字符串常量,那么使用String就足够了。​​ArrayList和linkedList的区别ArrayList和LinkedList都是Java中常用的集合类,它们的主要区别在于内部实现和性能。内部实现:ArrayList是基于数组(Array)实现的,而LinkedList是基于链表(Linked List)实现的。因此,对于对元素的随机访问,ArrayList比LinkedList更快;而对于插入、删除操作,LinkedList比ArrayList更高效。性能:由于ArrayList是基于数组实现的,它在随机访问方面表现非常优秀,但是在插入、删除元素时需要移动后面的元素,所以时间复杂度为O(n),其中n为元素数量。而LinkedList是基于链表实现的,插入、删除元素的时间复杂度为O(1),但是随机访问元素需要遍历整个链表,所以时间复杂度为O(n)。import java.util.ArrayList; import java.util.LinkedList; import java.util.Random; public class ArrayListExample { public static void main(String[] args) { // 创建ArrayList对象 ArrayList<Integer> list1 = new ArrayList<>(); list1.add(1); list1.add(2); list1.add(3); list1.add(4); list1.add(5); System.out.println("ArrayList: " + list1); // 创建LinkedList对象 LinkedList<Integer> list2 = new LinkedList<>(); list2.add(1); list2.add(2); list2.add(3); list2.add(4); list2.add(5); System.out.println("LinkedList: " + list2); // 添加元素到ArrayList中 Random random = new Random(); for (int i = 0; i < 10; i++) { int num = random.nextInt(10); if (list1.contains(num)) { continue; } else { list1.add(num); } } System.out.println("ArrayList: " + list1); // 添加元素到LinkedList中 for (int i = 0; i < 10; i++) { int num = random.nextInt(10); if (list2.contains(num)) { continue; } else { list2.add(num); } } System.out.println("LinkedList: " + list2); } }HashMap和HashTable的区别Java中的HashMap和HashTable都是用于存储键值对的数据结构,但它们之间有一些重要的区别。线程安全性:HashTable是线程安全的,而HashMap是非线程安全的。这意味着在多线程环境下使用HashMap时需要进行同步处理,否则可能会出现数据不一致的情况。性能:HashMap通常比HashTable更快,因为HashMap内部使用哈希表来实现,而HashTable则是基于数组实现的。Null值:HashMap允许Key和Value为null,而HashTable不允许。初始容量和增长因子:HashMap有固定的初始容量和增长因子,而HashTable没有。HashMap的初始容量为16,增长因子为0.75;而HashTable的初始容量为11,增长因子为0.75。​import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; public class MapExample { public static void main(String[] args) { // 创建HashMap对象 HashMap<Integer, String> map1 = new HashMap<>(); map1.put(1, "one"); map1.put(2, "two"); map1.put(3, "three"); System.out.println("HashMap: " + map1); // 创建HashTable对象 HashSet<Integer> set1 = new HashSet<>(); set1.add(1); set1.add(2); set1.add(3); System.out.println("HashTable: " + set1); } }​Collection包结构,与Collections的区别Java Collection包是Java集合框架的核心,它提供了一组接口和类来处理集合。Java Collection包结构包括以下几个部分:接口Java Collection包中定义了许多接口,例如List、Set、Map等。这些接口定义了集合的基本操作,例如添加元素、删除元素、查找元素等。类Java Collection包中定义了许多类,用于实现各种接口。例如,ArrayList类实现了List接口,LinkedList类实现了List接口并保持元素的插入顺序,HashMap类实现了Map接口,TreeMap类实现了Map接口并按照自然排序或自定义排序方式对键进行排序等。静态方法Java Collection包中还定义了一些静态方法,用于创建新的集合对象或对现有集合进行操作。例如,Collections.singletonList()方法可以创建一个只包含一个元素的List集合,Collections.emptyList()方法可以创建一个空的List集合,Collections.unmodifiableList(List list)方法可以将一个List集合转换为不可修改的集合等。import java.util.ArrayList; import java.util.List; public class Example { public static void main(String[] args) { // 创建一个List集合对象 List<String> list = new ArrayList<>(); list.add("apple"); list.add("banana"); list.add("orange"); // 输出List集合中的元素数量 System.out.println("List集合中的元素数量:" + list.size()); // 将List集合转换为字符串并输出 System.out.println("List集合转换为字符串:" + list); } }java 的四种引用,强软弱虚Java中四种引用是:Strong Reference(强引用):是指在程序中直接使用一个对象时所使用的引用,如果该对象没有其他强引用指向它,那么它会被垃圾回收器回收。Soft Reference(软引用):是指在程序中使用一个弱引用来引用一个对象,只有在内存不足时才会被回收。Weak Reference(弱引用):是指在程序中使用一个虚引用来引用一个对象,只要任何地方有一个强引用指向它,它就不会被回收。Final Reference(最终引用):是指在程序中使用一个永久引用来引用一个对象,无论何时何地,只要存在这个引用,对象就不会被回收。// 创建一个对象并使用Strong Reference引用它 Object obj = new Object(); ReferenceType objRef = new ReferenceType(obj); System.out.println("obj的值为:" + obj.toString()); System.out.println("objRef的值为:" + objRef.get()); // 将objRef设置为null,表示不再使用Strong Reference引用它 objRef = null; System.gc(); // 执行一次垃圾回收 System.out.println("obj的值为:" + obj.toString());​泛型常用特点Java泛型是Java编程语言的一个重要特性,它允许程序员在编译时为数据类型指定通用类型参数。以下是Java泛型的一些常用特点:类型安全:泛型可以确保在运行时不会发生类型不匹配的问题,从而提高程序的稳定性和安全性。代码重用:通过使用泛型,程序员可以将相同的代码用于不同类型的数据结构,从而提高代码的复用性。类型擦除:Java泛型实现了类型擦除,这意味着在运行时,实际使用的是原始类型,而不是泛型类型。这有助于减少内存开销,提高性能。public class GenericClass<T> { private T value; public void setValue(T value) { this.value = value; } public T getValue() { return value; } } public class Main { public static void main(String[] args) { GenericClass<Integer> integerClass = new GenericClass<>(); integerClass.setValue(10); System.out.println("Value of integerClass: " + integerClass.getValue()); // Output: Value of integerClass: 10 GenericClass<String> stringClass = new GenericClass<>(); stringClass.setValue("Hello, World!"); System.out.println("Value of stringClass: " + stringClass.getValue()); // Output: Value of stringClass: Hello, World! } } 我们定义了一个名为GenericClass的类,它具有一个泛型类型参数T。然后我们创建了两个不同的GenericClass实例:integerClass和stringClass,它们分别使用了整数和字符串作为泛型类型参数Java创建对象有三种方式:使用new关键字创建对象 java复制代码Person person = new Person("张三", 20);使用反射机制创建对象 java复制代码Class<?> clazz = Class.forName("com.example.Person"); Object obj = clazz.newInstance();使用工厂模式创建对象 java复制代码// 定义一个工厂类,用于创建Person对象 public class PersonFactory { public static Person createPerson(String name, int age) { Person person = new Person(name, age); return person; } } // 在其他地方调用工厂类创建Person对象 Person person = PersonFactory.createPerson("张三", 20);​有没有可能两个不相等的对象有相同的hashcode在Java中,如果两个对象的hashCode()方法没有正确实现,那么它们可能会有相同的hashCode值。这是因为hashCode()方法的目的是生成一个整数,用于标识对象在哈希表中的位置,而哈希表使用的是散列算法(如MD5或SHA-1),这些算法并不要求不同的对象具有不同的哈希码值。 java复制代码 public class UnequalHashCodeExample { public static void main(String[] args) { String str1 = "Hello"; String str2 = "World"; int hashCode1 = str1.hashCode(); int hashCode2 = str2.hashCode(); System.out.println("str1 hash code: " + hashCode1); System.out.println("str2 hash code: " + hashCode2); if (hashCode1 == hashCode2) { System.out.println("The two strings have the same hash code!"); } else { System.out.println("The two strings do not have the same hash code!"); } } }在这个例子中,我们创建了两个字符串对象:str1和str2,它们的内容分别为"Hello"和"World"。然后,我们分别调用它们的hashCode()方法并打印结果。由于这两个字符串的内容不同,我们期望它们的哈希码也不同。然而,当我们运行这段代码时,我们会发现它们确实具有相同的哈希码值(输出结果为:"The two strings have the same hash code!"),这说明在某些情况下,两个不相等的对象确实可能具有相同的哈希码。深拷贝和浅拷贝的区别是什么?Java中的深拷贝和浅拷贝都是对象复制的方式,它们的区别在于是否对原始对象及其引用类型进行递归复制。浅拷贝(Shallow Copy): 浅拷贝只复制对象本身及其基本类型属性(如int、float等),而不复制引用类型属性(如String、List等)所指向的对象。换句话说,浅拷贝会创建一个新的对象,但是新对象的引用类型属性仍然指向原始对象。这意味着在原始对象中修改引用类型属性时,新对象的引用类型属性也会受到影响。 public class ShallowCopyExample { public static void main(String[] args) { List<Integer> originalList = new ArrayList<>(Arrays.asList(1, 2, 3)); List<Integer> shallowCopyList = new ArrayList<>(originalList); // 浅拷贝 originalList.add(4); // 在原始列表中添加元素4 System.out.println("Original list: " + originalList); // [1, 2, 3, 4] System.out.println("Shallow copy list: " + shallowCopyList); // [1, 2, 3, 4] } }深拷贝(Deep Copy): 深拷贝会递归地复制原始对象及其引用类型属性所指向的对象。这意味着在原始对象中修改引用类型属性时,新对象的引用类型属性不会受到影响。​import java.util.ArrayList; import java.util.List; public class DeepCopyExample { public static void main(String[] args) { List<Integer> originalList = new ArrayList<>(Arrays.asList(1, 2, 3)); List<Integer> deepCopyList = new ArrayList<>(originalList); // 深拷贝 originalList.add(4); // 在原始列表中添加元素4 System.out.println("Original list: " + originalList); // [1, 2, 3, 4] System.out.println("Deep copy list: " + deepCopyList); // [1, 2, 3, 4] } }​
  • [技术干货] 网络爬虫—js逆向详讲与实战-转载
     js逆向 📑 📑在这个大数据时代,我们眼睛所看到的百分之九十的数据都是通过页面呈现出现的,不论是PC端、网页端还是移动端,数据渲染还是基于html/h5+javascript进行的,而大多数的数据都是通过请求后台接口动态渲染的。而想成功的请求成功互联网上的开放/公开接口,必须知道它的URL、Headers、Params、Body等数据是如何生成的。  JavaScript逆向的详细讲解 📑 📑JavaScript逆向工程是指通过分析JavaScript代码和运行行为来理解程序的内部机制。这种技术可以用于破解JavaScript程序的加密和混淆,以及获取程序的逻辑和数据等信息。 以下是JavaScript逆向的详细讲解:  1. JavaScript逆向工程的基本原理  📑 📑JavaScript逆向工程的基本原理是通过分析JavaScript代码和运行行为来理解程序的内部机制。这种技术可以用于破解JavaScript程序的加密和混淆,以及获取程序的逻辑和数据等信息。 JavaScript逆向工程通常包括以下步骤:  1)获取JavaScript代码: 可以使用浏览器的开发人员工具或其他工具来获取JavaScript代码。  2)分析JavaScript代码: 可以使用代码编辑器或其他工具来分析JavaScript代码,包括查找函数、变量、常量和操作符等。  3)调试JavaScript代码: 可以使用浏览器的开发人员工具或其他工具来调试JavaScript代码,包括断点调试、单步调试和变量监视等。  4)破解JavaScript代码: 可以使用反混淆和反编译工具来破解JavaScript代码,以获取程序的逻辑和数据等信息。  2. JavaScript逆向工程的应用场景  JavaScript逆向工程可以应用于以下场景:  1)破解加密和混淆的JavaScript程序:JavaScript逆向工程可以破解加密和混淆的JavaScript程序,以获取程序的逻辑和数据等信息。  2)调试和测试JavaScript程序:JavaScript逆向工程可以帮助开发人员调试和测试JavaScript程序,以发现程序中的错误和问题。  3)优化JavaScript程序的性能和安全性:JavaScript逆向工程可以帮助开发人员优化JavaScript程序的性能和安全性,以提高程序的质量和可靠性。  4)研究JavaScript程序的内部机制:JavaScript逆向工程可以帮助研究人员研究JavaScript程序的内部机制,以发现其中的漏洞和安全问题。  3. JavaScript逆向工程的注意事项  在进行JavaScript逆向工程时,需要注意以下事项:  1)遵守法律法规: JavaScript逆向工程可能涉及版权、知识产权和隐私等问题,需要遵守相关的法律法规。  2)保护个人隐私: 在分析JavaScript程序时,需要遵守个人隐私的原则,不得获取个人信息和敏感信息。  3)避免滥用JavaScript逆向技术: JavaScript逆向技术可以用于破解和攻击,需要避免滥用。  4)保护JavaScript程序的安全性: 在进行JavaScript逆向工程时,需要保护JavaScript程序的安全性,不得泄露JavaScript程序的机密信息和漏洞。  5)学习和研究JavaScript逆向技术: JavaScript逆向技术是一种有用的技术,需要学习和研究,以提高自己的技能和知识水平。  实战是学习知识最快的途径,下面进行实战演示帮助理解学习。  实战演示 有道翻译 有道翻译  浏览器:谷歌浏览器   右键检查,输入需要翻译的内容,然后开始抓包    🎯点击翻译,我们得到两个数据,一个是key,请求方式是post,状态是200 载荷是西瓜,预览里面出现success,表示翻译成功。    🎯还有一个文件是webtranslate,请求方法同样是post,状态是200,载荷里面有西瓜两个字和一些参数,预览和响应里面是一串加密的数据。  🎯接下来敲代码来获取文件,然后来破解数据  # coding = utf-8 import crawles  url = 'https://dict.youdao.com/webtranslate'  cookies = {     'OUTFOX_SEARCH_USER_ID': '-312652410@10.108.162.134',     'OUTFOX_SEARCH_USER_ID_NCOO': '42958927.495580636', }  headers = {     'Accept': 'application/json, text/plain, */*',     'Accept-Language': 'zh-CN,zh;q=0.9',     'Cache-Control': 'no-cache',     'Connection': 'keep-alive',     'Content-Type': 'application/x-www-form-urlencoded',     'Origin': 'https://fanyi.youdao.com',     'Pragma': 'no-cache',     'Referer': 'https://fanyi.youdao.com/',     'Sec-Fetch-Dest': 'empty',     'Sec-Fetch-Mode': 'cors',     'Sec-Fetch-Site': 'same-site',     'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36',     'sec-ch-ua': '\"Google Chrome\";v=\"113\", \"Chromium\";v=\"113\", \"Not-A.Brand\";v=\"24\"',     'sec-ch-ua-mobile': '?0',     'sec-ch-ua-platform': '\"Windows\"', }  data = {     'i': '西瓜',     'from': 'auto',     'to': '',     'domain': '0',     'dictResult': 'true',     'keyid': 'webfanyi',     'sign': 'f522e5818a8497d9a329a93a522eaa2e',     'client': 'fanyideskweb',     'product': 'webfanyi',     'appVersion': '1.0.0',     'vendor': 'web',     'pointParam': 'client,mysticTime,product',     'mysticTime': '1683270687293',     'keyfrom': 'fanyi.web', }  response = crawles.post(url, headers=headers, data=data, cookies=cookies) print(response.text) 运行结果:  🎯接下来我们可以通过多次发多次请求来观察哪些是变的,哪些是不变的数据:   🎯经过对比,我们发现,sign,mysticTime这两个字段的参数是动态变化的。  🎯参数大概分析之后,我们就找出对应的js文件,来分析一下js是如何处理的参数 选择文件,点击启动器,然后可以随便点击一个文件,然后点击它。   🎯输入sign后,如果出现多个,我们需要逐个观察,看哪一个符合要求  🎯我们在sign这里打上断点,然后点击翻译,进行抓包处理,得到e和t的值  e: "fsdsogkndfokasodnaso" t: 1683272866426  🎯然后我们点击其他参数,获取数据    🎯接下来我们写代码,来得到sign的值  import time t = 'fsdsogkndfokasodnaso' e = time.time() e = 1682603344052 d = 'fanyideskweb' u = 'webfanyi'  data = f'client={d}&mysticTime={e}&product={u}&key={t}'  from hashlib import md5 m = md5() m.update(data.encode('utf-8')) nonce = m.hexdigest() print(nonce)  🎯接下来对字符串进行解密,将解密后的字节码转换为utf-8编码的文本字符串。  import hashlib from Crypto.Cipher import AES from Crypto.Util.Padding import unpad import base64 # 导入必要的模块和库  # pip install pycryptodome # 将存放模块的文件(Crypto)改成大写开头(Crypto)  def decrypt( decrypt_str):     key = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl"     iv = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4"      key_md5 = hashlib.md5((key).encode('utf-8')).digest()     iv_md5 = hashlib.md5((iv).encode('utf-8')).digest()     print('key_md5:', key_md5)     print('iv_md5:', iv_md5)     print()     aes = AES.new(key=key_md5, mode=AES.MODE_CBC, iv=iv_md5)      code = aes.decrypt(base64.urlsafe_b64decode(decrypt_str))     return unpad(code, AES.block_size).decode('utf8')  print(decrypt(response.text)) 设置密钥和初始向量 key = "ydsecret://query/key/B*RGygVywfNBwpmBaZg*WT7SIOUP2T0C9WHMZN39j^DAdaZhAnxvGcCY6VYFwnHl" iv = "ydsecret://query/iv/C@lZe2YzHtZ2CYgaXKSVfsb7Y4QWHjITPPZ0nQp87fBeJ!Iv6v^6fvi2WN@bYpJ4" 这里定义了两个变量key和iv,分别表示密钥和初始向量。注意到这两个字符串已经被加密处理,因此在使用之前需要将它们进行解密。  对密钥和初始向量进行哈希处理 key_md5 = hashlib.md5((key).encode('utf-8')).digest() iv_md5 = hashlib.md5((iv).encode('utf-8')).digest() 使用了哈希函数md5对密钥和初始向量进行处理。在处理之前,需要将密钥和初始向量从字符串类型转换为字节类型,并在处理后获取到它们的哈希值。  创建AES对象并解密消息 aes = AES.new(key=key_md5, mode=AES.MODE_CBC, iv=iv_md5)  code = aes.decrypt(base64.urlsafe_b64decode(decrypt_str)) 创建了一个AES对象,使用了上一步中得到的哈希值作为密钥和初始向量的值,并使用CBC模式进行加密解密操作。然后,我们对传入的待解密字符串进行base64解码,再使用解密过程对其进行解密操作。  移除padding并返回结果 return unpad(code, AES.block_size).decode('utf8') 1 通过Crypto.Util.Padding.unpad函数移除了解密后的字节码中的padding,并通过.decode('utf8')将其转换为文本字符串类型。最终,我们从decrypt函数中返回了解密后的明文字符串。  🎯这段代码主要实现了一个AES-CBC加密算法的解密过程,使用了哈希函数增强了密钥和初始向量的安全性,并通过base64编解码和padding移除等操作对加密消息进行了处理。  运行结果如下:  ———————————————— 版权声明:本文为CSDN博主「以山河作礼。」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_50804299/article/details/130424815 
  • [技术干货] 【实战问题解决2/50】对WPS已经打开的文件进行操作/VBS操作WPS的几个方法分享
    背景某项目开发时需要操作EBS系统,此EBS系统在测试环境可以到处xlsm模板,xlsm模板会动态加载数据库数据,并且可以另存为xlsm文件,使用excel的相关操作可以对xlsm模板里的数据进行修改并提交。但是在正式环境中,打开另存的文件后需要登录系统鉴权,这里的登录始终通不过,所以只能操作打开的xlsm文件,因为是使用的WPS企业版,直接操作WPS的页面又无法实现,项目开发陷入僵局。解决思路后来经过查阅资料,发现VBS可以操作excel文件,经过测试,VBS可以找到已经被WPS打开的文件,并且对齐进行操作。所以就有了如下的内容。先从一个文件读取到需要的数据范围,然后粘贴到当前打开的WPS文件的指定范围。至此,问题得以解决,并且执行速度、准确性、以及演示效果非常好。VBS是VB Script,是使用VB的语法写的脚本,可以完成某些特殊的场景。1、excel使用需要把“KET”改成“Excel”,这是从文件里读取一个range的内容到内存中。2、把内存的内容粘贴到当前已经打开的xls文件的位置3、VBS文件带入参allfile = WScript.Arguments(0) sheetName = WScript.Arguments(1)更多内容可以参考附件
  • [问题求助] 关于weautomate“抓取网页数据”组件,只能抓取一个相似数据,如何才能抓取多个相似数据。十分感谢!
    如题,比如我要抓某网站的数据,要同时抓名称、评论条数、浏览次数等的信息,如何才能一次抓取我想要的所有内容。谢谢大家!
  • [技术干货] RPA 实战:让小姐姐填满你的硬盘(上)
    # RPA 实战:让小姐姐填满你的硬盘(上) > 某天,我无意逛到某个小网站,如果我不懂代码,想要网站上全部的妹子,我该怎么做?……幸好有她,她好我也好,她就是 RPA -- Robotic Process Automation,中文意思是机器人流程自动化,换句话说就是利用机器人技术来实现业务流程的自动化处理。 ## 环境安装 首先打开另一个小网站 -- [https://www.hwtelcloud.com/products/rpa](https://www.hwtelcloud.com/products/rpa),下载【设计器】,并进行使用激活;下载【执行器】,让程序自己动;此外还需下载浏览器驱动和安装浏览器插件。关于软件的下载安装等此处就不进行讲解,相信您能搞定! ![RPA 实战图片爬取](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/230307avmzpcwjk7kltabc.png) 本大狮发表文章时所用到的软件版本及环境如下: - WeAutomate Studio(2.14.0) - WeAutomate Assistant(2.14.0) - VM: python 3.8.5 64-bit - Windows Feature Experience Pack 421.16300.0.3 - Google Chrome 91.0.4472.124 64-bit ![RPA 实战图片爬取](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/2303321rqtxldzkktbg9pc.png) 将近1000页的帮助文档在 WeAutomate Studio 安装目录下:`document/RPA_Robot_Development_Guide_zh_cn.pdf` ![RPA 实战图片爬取](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/230400qubwpdzvozarso4a.png) 此外还有更多优质的课程尽在华为云学院: [https://ilearningx.huawei.com/portal/courses?q=ANTROBOT](https://ilearningx.huawei.com/portal/courses?q=ANTROBOT) ## “技术”解析 说到图片,我最先想到的是某度图片,之前通过使用 NodeJS 爬虫爬取过该网站搜索出来的图片,大概的流程如下: 1. 输入关键字,`//*[@id="kw"]`这里的 Input 标签就是我们关键字输入的位置。 ![RPA 实战图片爬取](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/225617tfzunwlwpupu9twg.png) 2. 获取图片列表,当我们输入关键字并搜索的结果就呈现为页面的`//*[@id="imgid"]/div/ul/li[1]/div/a/img`,也就是 classname 为 `main_img` 的图片元素 ![RPA 实战图片爬取](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/225658znztafhadpbsfew5.png) 3. 滚动窗口,当我们模拟鼠标滚动时,页面会加载更多的图片。 ![RPA 实战图片爬取](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/225737ksx2pf04ogjykeil.png) 4. 图片下载,我们获取到每个图片元素的 `src` 属性,就是我们最终的图片下载地址。 ![RPA 实战图片爬取](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/230443dsl5l6mw7ybzdyes.png) 那么整个流程下来,在 RPA 中我们需要用到哪些控件呢?首先我们要从网页获取图片,`打开网页`控件是第一个用到的;其次我们要获取网页的高度并进行滚动、获取页面图片元素及`src`属性,我们可以通过`执行 js 代码`控件来帮我们完成;此外,涉及的一些流程控制我们必须依赖分支、循环等控件;最后下载图片则通过`下载文件`控件实现。 ![RPA 实战图片爬取](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/230508sa6pnbxejfpuoi4r.png) ## 实施过程 RPA 的实施一般遵循下面的生命周期: ![RPA 开发流程图](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/230532mcbsjoy32qj10ddn.png) ### RPA 评估 首先 RPA 评估,针对此次我的这个“大胆”的想法,我的整个业务流程完成能够用自动化来实现,当然投资回报率(ROI)对我来说,完全只需投入少量的时间就能获取到海量的数据,理论上是非常划算的。本次实践不对 RPA 工具本身做任何评价,本着新手小白的学习态度,一款好用的强大的称手的 RPA 工具请按照自身的场景去选取,如同技术选型,对开发工具做一定的调研、体验才能更好的选择合适的工具。 ### RPA 设计 接着 RPA 设计,我们先梳理一下完整的业务流程,细粒度精确到每一个点,比如网页数据怎么获取、图片怎么下载等等,势必会涉及到一些逻辑,比如分支逻辑、循环逻辑;除此之外,我们需要明确每个操作的输入和输出;在实际工作中,我们可能还需形成具体的文档。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/230613zthdma9iwv9vijbq.png) ### RPA 开发 简单梳理完流程之后,开始开发我的第一个 RPA 程序。尽管整个流程梳理起来非常简单,但实际开发的时候,由于对工具、编程语言基础等不熟练,我踩了一个又一个的坑,最终走出了结合 JS 代码以及 NodeJS 爬虫的思路来开发这个图片爬取的 RPA 程序。 - 1. 全局变量定义 由于本大狮功力不够,暂时没能实现打开网页之后动态获取跳转的链接,因此我直接使用的输入关键字搜索之后的 url 作为全局变量 url 的值,拼接 keyword 来实现结果页面图片的获取。 | 名称 | 类型 | 值 | 描述 | | --- | --- | --- | --- | | url | String | (根据实际情况填写) | baseurl,用于拼接 keyword 访问结果页 | | keyword | String | (根据实际情况填写) | 关键字,比如"性感美女"、“靓仔” | | height | String | 0 | 初始的窗口高度,用于缓存过程数据 | | num | Number | (根据实际情况填写) | 图片数量,暂未做精确处理,作为退出循环的条件 | ![RPA 实战图片爬取.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/230635mjmcggub9fqkvjve.png) - 2. 打开网页 写得比较随意,中文在浏览器地址中可以做 url 编码处理,这里可以引入 python 模块哦! ![RPA 实战图片爬取.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/2307013w0nptoofioadphr.png) - 3. 获取浏览器窗口高度 建议此操作前先调用控件让浏览器窗口最大化,然后通过 JS 代码获取浏览器窗口高度并赋值给 `height`,方便后面的滚动操作。`执行 JS 代码`控件是有返回值的,在代码中通过 `return` 可以返回指定内容。 ```js return document.body.offsetHeight; ``` ![RPA 实战图片爬取.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/230718vagofpmnudddaxp7.png) - 4. 循环操作 此处模拟了 JS 的定时器方法,使用`While`来进行滚动操作的循环。假如我想获取 10k 张图片,第一次加载的结果可能才几十张,如果是人工操作的话,我需要不断滚动浏览器以加载获得更多的数据。那机器人是怎么自动化操作呢?我们给它设置一个延时操作,然后调用 JS 代码滚动浏览器,同时更新`height`: ```js let height=@{height}*2; window.scrollTo(0,height); return height; ``` ![RPA 实战图片爬取.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/2307449kyf5bqv1cs1uulw.png) - 5. 获取图片链接 同样的通过调用`执行 JS 代码`控件来获取图片的链接和数量,为图片下载和退出循环做准备。 ```js let arrs = document.querySelectorAll('.main_img'); let urls = [] for (let img of arrs){urls .push(img.src)} return urls; ``` ![RPA 实战图片爬取.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/230758wvklhkjm65sk1vts.png) - 6. 退出循环 比较预期的图片数量和实际的图片数量,如果图片够了,我们就退出循环,进行下一步操作。 ![RPA 实战图片爬取.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/230810sw4atuuuzdsdapns.png) - 7. 创建文件夹 这里的步骤非必须,根据实际情况取舍,我这里是简单的尝试并熟悉 RPA 工具;不过逻辑还不够严谨,可以优化的点实在是太多。 ![RPA 实战图片爬取.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/2308222nlyviwmew51xp8z.png) - 8. 遍历图片链接并下载 “没有什么问题是一个 for 循环不能解决的,如果有,那就两个”,此处能够直接进行循环是因为之前 JS 代码返回的值已经是 list 了,因此将 url 遍历出来调用下载文件控件就能直接下载。 ![RPA 实战图片爬取.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/230835sfi90tidqypnltse.png) 至此,一个简单的图片爬取 RPA 程序已经开发完毕。 ### RPA 测试 如果是大型的项目,必然会经过单元测试、集成测试等全面的测试历练之后才会发布。而对于此次的 **RPA 图片爬取**小程序,我只做了简单的调试,理论上调试依旧属于开发阶段,严格来说需要模拟各种场景、各种系统和上下文进行正确性测试等,测试完成后才可以上线。 作为伪测试,我输入**华为美女** 100 张,结果程序给了我 130 张,XDM 你们说测试通过吗? ![RPA 实战图片爬取.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202107/15/2308501xrtveijovtity06.png) ### RPA 优化 毕竟是 Hello World 级别的程序,出自 HW 攻城狮之手,因此有很多地方需要完善,比如逻辑的严谨性、没有考虑边界、下载的文件名杂乱等。毕竟真实的项目如果是这么个水平,估计…… RPA 的优化可以从最优设计、从性能、从实际业务需求等方面去实施。当然,屏幕前的您一定有自己的优化思路,期待您与我分享! ## 小结 回到标题,咦,为什么是上篇,难道还有下篇?敬请期待吧!那么上篇讲了哪些知识点呢?首先是 RPA 环境搭建及入门,然后是 RPA 程序开发的套路,接着是网页元素处理、文件下载、如何调用 python 模块、如何调用 JS 等,最后是简单测试及优化建议。感谢阅读,希望能帮到您! 最后,附送几个G的学习资料: [https://ilearningx.huawei.com/portal/courses?q=ANTROBOT](https://ilearningx.huawei.com/portal/courses?q=ANTROBOT) **如果您对本文有任何意见,欢迎与我联系,公众号:[胡琦](https://gitee.com/hu-qi/cdn/blob/master/me/wechat-huqi.jpg), Wechat: [Hugi66](#)**