精华内容
下载资源
问答
  • 手把手写Demo系列之车道线检测

    千次阅读 2021-03-27 00:27:04
    点击上方“3D视觉工坊”,选择“星标”干货第一时间送达文章导读本文是一篇从零开始做车道线检测Demo的教学式文章,从场景的定义到模型微调的输出,描述车道线Demo式例程中在每个环节需要做的...

    点击上方“3D视觉工坊”,选择“星标”

    干货第一时间送达

    文章导读

    本文是一篇从零开始做车道线检测Demo的教学式文章,从场景的定义到模型微调的输出,描述车道线Demo式例程中在每个环节需要做的工作,以及中途可能会遇到的各种问题。

    1

    场景维度定义

    根据任务需求,需要对其进行维度划分以便后续数据采集和模型评估。如果我们做一个全场景的车道线检测任务,需要考虑:

    • 时间维度:白天、晚上

    • 天气维度:晴朗、阴天、下雨

    • 应用场景:高速道路、城市道路、乡镇小路

    • 目标类别:长实线、双黄线、虚线等等

    • 难易情况:道路清晰、数据遮挡、车道线磨损等

    数据采集之前必须明确任务需求,在什么场景下能够解决什么样的问题?做Demo不可能一个模型适用所有自然场景。

    2

    数据采集清理

    根据设定好的需求场景,以视频录制的方式采集,一般需要以下几个步骤:

    • 视频录制

    • 间隔抽帧

    • 相似性过滤

    • 数据分类

    • 数据筛选

    视频录制过程中,如果使用的是USB等免驱的摄像头,直接调用OpenCV的VideoCapture接口打开即可;如果使用的是车载相机需要购买配套的解串套件或者根据摄像头的串行编码,对应完成解串功能获取Raw Data数据。

    间隔抽帧这一步考虑到视频如果按照30FPS的帧率录制,在每一帧之间的变化很小,特征差异不大,将这些图片全部进行后续的标注会增加很大的人力成本,所以通常以5s/帧的方式进行帧间采用,推荐使用ffmpeg指令,简单方便,对于画质、采样率、采用周期等均可以参数调节:

    ffmpeg -i out.avi -r 5 -f image2 image-%05d.jpeg

    相似性过滤用于解决车辆反复行驶在相同路段情况、车速在行驶过程中有快有慢,甚至遇到红绿灯时停车小一分钟的情况,间隔抽帧后的数据中仍然有很多重复或者相似的图片。

    图像之间的相似性度量方法很多,传统算法一般采用特征提取+距离度量,根据阈值判断;深度学习方法把上述两步封装在端到端的网络中。考虑到这里仅是预处理的环节,采用类似直方图统计、感知哈希、结构相似性等类似算法即可,针对车道线检测的任务, 关注的区域在地面上,所以需要将感兴趣区域限定在图像下方(下图绿色直线下方):

    数据分类是为了匹配场景维度的定义,将不同场景或者类型的图片数据放在定义好的文件夹中,观测数据量在类别之间的平衡,有利于后续有针对性的填充数据。比如将城市道路的数据和高速道路数据分开存放,白天和晚上的数据分开存放等。

    数据筛选的细致与否会影响到后续网络模型的收敛以及精度优劣,虽然神经网络具有较强的特征提取能力,对场景有一定鲁棒性,但是在训练数据中添加太多的噪声也会提升网络学习难度(毕竟写Demo阶段,并没有海量数据能够把噪声淹没),训练的数据希望是如下车道线清晰,光照良好的数据:

    但是实际上自然场景采集的图片数据存在很多噪声,比如拥堵路段的车道线遮挡严重,如果车道线网络模型采用像素级分类的思路搭建,对以下图片的标注就比较困难;如果车道线网络模型上升到抽象的语义空间,根据上下文的环境信息预测车道线空间位置,下图倒也可以使用。

    除了车道线被遮挡的问题,在城市路况中存在施工或很久未翻修的老路,道路线条磨损严重,很多情况人肉眼都无法区分,建议删除这一部分训练数据:

    当然也有很多本来就没有车道线的路况,此类数据需要根据数据转换的程序来决定去留,在数据转换时仅少数代码未加判断会导致程序崩溃:

    检查数据时还会遇到在拱桥下,进出隧道等光照昏暗或者曝光严重的情况,造成车道线辨别率降低,如下图所示:

    当然你要问实际工程上如何解决这些问题,无外乎从软件角度怎么增加算法模型的鲁棒性;从硬件角度如何定制传感器接受更多光子以及优化ISP算法。

    3

    样本标注转换

    本次车道线检测的历程,我们采用实例分割的方式做逐像素点的分类。可以采用开源的标注工具,比如labelme,标注精灵等。

    第一步:根据模型的不同,输入数据的类型也存在差异,这里我们采用划线的方式进行标注,如下图所示,因为是实例分割,不同的车道标注不同的类别:

    如果采用labelme进行标注,生成的是json格式的标签数据,如下图所示,标签中以折点的形式存储:

    第二步:使用labelme自带的功能函数转换成实例图等信息,其中包括原始图片,实例图,可视化图等:

    第三步:由于搭建的网络如果输入原始图像、语义图像、实例图像三种,所以需要将json转换后的数据通过脚本生成TuSimple数据集的形式,得到如下形式,三个文件夹分别存放二值图,原始图,示例图,train.txt用于存放训练数据列表,val.txt用于存放验证数据列表:

    4

    模型预训练

    基于深度学习的方法造轮子的成本一般很高,需要经历大量的模型修改、参数调节的反复试错过程。不仅对服务器硬件平台有较高的要求,也考验算法工程师自身的知识积淀。

    如何快速高效的写Demo呢?

    • 现有网络模型

    • 开源数据集

    • 自己数据微调

    之前有篇文章具体聊过车道线检测的网络,大多数是基于像素的分割模型,也有一些基于检测方法的车道线检测,常用的开源模型相关论文如下(可以找小编要或者等后续上传云盘):

    车道线的开源数据集有很多,比如:

    • Caltech

    • VPGNet

    • TuSimple

    • CULane

    • BDD100k

    • ApolloScape

    • CurveLanes

    TuSimple数据集位于高速场景,天气晴朗,车道线较为清晰,适用于高速,高架等应用场景的Demo制作:

    CULane数据集包含拥挤,黑夜,暗影等八种难以检测的情况,适合补充TuSimple数据集提升难例的精度:

    因为TuSimple和CULane数据量均不大,适合普通服务器或者本机上的预研式训练。推荐将两个数据集的数据统一进行模型训练,得到一份预训练的权重。

    更为简单的方法即是下载开源模型提供的一些权重,省去了自己训练的环节,不过这样会大大降低模型修改的灵活度,现在的分割网络一般基于encoder-decoder的形式搭建,为了加速推理时间或者提供预测精度,通常会对开源网络做些修改:

    • 采用轻量化模型替换特征提取网络;

    • 减少上采样次数,替代掉全分辨率的输出;

    • 增加ASPP或者Attention机制

    模型结构变化后就无法使用源码中提供的预训练权重,所以后续微调的灵活度出发,自己用开源数据集做训练的方法最佳。

    5

    Fintune训练

    Fintune阶段一般就两步:

    • 加载预训练模型

    • 加载自己的数据继续训练

    加载预训练模型时,可以考虑是否需要冻结encoder层或者只释放最后几层进行参数更新,当然也可以不冻结模型,全部进行微调。根据开源数据和自己数据的比例及差异而决定:

    • 开源数据量远大于自己的数据并且类型差异不大,建议只释放网络后面的几层进行微调;

    • 开源数据量仅比自己的数据多一个量级但是类型差异较大,建议参数全部参与训练;

    加载自己的数据训练,期望是损失不断下降最后收敛,不过大多数时候属于——期望很美好,现实很骨感。可能会遇到:

    • 实例损失居高不下;

    • 训练开始梯度爆炸;

    • 模型震荡不收敛等;

    可以尝试以10倍递减降低学习率;修改学习率衰减策略;修改网络实例分割部分的聚类参数;最最最重要的是检查数据和标签

    由于选用的车道线检测网络的实例分割部分会根据点到不同车道线的距离来迭代拉近类内距离,放大类间距离,所以如果数据中的不同车道线在远处交织在一起,就很容易造成实例损失的异常,如下图所示:

    通过进一步过滤标签数据,或者做截断,通过后处理的曲线拟合来弥补前方损失的一小段车道。

    最后训练收敛后用验证集做评估,或者直接拉到路上进行测试,毕竟做Demo,针对实际场景不好的数据再收集回来做数据填充迭代。

    本文仅做学术分享,如有侵权,请联系删文。

    下载1

    在「3D视觉工坊」公众号后台回复:3D视觉即可下载 3D视觉相关资料干货,涉及相机标定、三维重建、立体视觉、SLAM、深度学习、点云后处理、多视图几何等方向。

    下载2

    在「3D视觉工坊」公众号后台回复:3D视觉github资源汇总即可下载包括结构光、标定源码、缺陷检测源码、深度估计与深度补全源码、点云处理相关源码、立体匹配源码、单目、双目3D检测、基于点云的3D检测、6D姿态估计源码汇总等。

    下载3

    在「3D视觉工坊」公众号后台回复:相机标定即可下载独家相机标定学习课件与视频网址;后台回复:立体匹配即可下载独家立体匹配学习课件与视频网址。

    重磅!3DCVer-学术论文写作投稿 交流群已成立

    扫码添加小助手微信,可申请加入3D视觉工坊-学术论文写作与投稿 微信交流群,旨在交流顶会、顶刊、SCI、EI等写作与投稿事宜。

    同时也可申请加入我们的细分方向交流群,目前主要有3D视觉CV&深度学习SLAM三维重建点云后处理自动驾驶、多传感器融合、CV入门、三维测量、VR/AR、3D人脸识别、医疗影像、缺陷检测、行人重识别、目标跟踪、视觉产品落地、视觉竞赛、车牌识别、硬件选型、学术交流、求职交流、ORB-SLAM系列源码交流、深度估计等微信群。

    一定要备注:研究方向+学校/公司+昵称,例如:”3D视觉 + 上海交大 + 静静“。请按照格式备注,可快速被通过且邀请进群。原创投稿也请联系。

    ▲长按加微信群或投稿

    ▲长按关注公众号

    3D视觉从入门到精通知识星球:针对3D视觉领域的知识点汇总、入门进阶学习路线、最新paper分享、疑问解答四个方面进行深耕,更有各类大厂的算法工程人员进行技术指导。与此同时,星球将联合知名企业发布3D视觉相关算法开发岗位以及项目对接信息,打造成集技术与就业为一体的铁杆粉丝聚集区,近3000星球成员为创造更好的AI世界共同进步,知识星球入口:

    学习3D视觉核心技术,扫描查看介绍,3天内无条件退款

     圈里有高质量教程资料、可答疑解惑、助你高效解决问题

    觉得有用,麻烦给个赞和在看~  

    展开全文
  • 作者:曾 单位:东莞市城建规划设计院 一、需求分析 1、工业保护线简述 为贯彻落实《广东省降低制造业企业成本支持实体经济发展的若干政策措施》文件精神,支持实体经济发展,保障产业用地发展空间,东莞市...

    参赛单元:传统GIS数据处理

    作者:曾星

    单位:东莞市城建规划设计院

     

    一、需求分析

    1、工业保护线简述

    为贯彻落实《广东省降低制造业企业成本支持实体经济发展的若干政策措施》文件精神,支持实体经济发展,保障产业用地发展空间,东莞市政府划定严格控制和保护的工业用地范围线。划定工业保护线分为两类:工业红线和工业蓝线。工业红线是为保障城市产业长远发展而确定的,符合城市规划的工业用地保护线;工业蓝线是为了稳定城市一定时期内工业用地总规模,将已改变规划用途的现状工业用地择优划定的保护线。同时为工业保护线划定一事一议项目的更新模式以打消市场对工业用地的不合理预期。

    2、工业保护线入库的必要性

    工业保护线规模大,全市共划定420平方公里工业保护线,其中工业红线372平方公里,工业蓝线48平方公里;一事一议项目142宗,面积16 平方公里。工业保护线承载信息多,每宗工业保护线的属性数据应包含:图斑编号、所属镇街、所属类型、面积、基期工业用地比例、工业用地比例、新型产业用地比例、更新类型、更新时间、更新次数等。一事一议项目属性数据应包含:属地镇人民政府(街道办事处、园区管委会)承诺的改造模式及其他改造要求。东莞增量用地少,存量用地调整次数多,现状工业基数大,市场对工业用地存有不合理幻想等因素,潜在调整、管理问题较多。

    为规范和细化全市工业保护线的管理、维护及调整工作,需对工业保护线进行标准化入库,建立工业保护线信息化数据库并进行动态跟踪与维护。

    二、技术路线

    1、技术需求

    为方便管理和维护工业保护线数据库,库内需生成三类要素文件:工业保护线(实时库)、一事一议项目、工业保护线(历史库)。

    考虑到工业保护线调整历次范围会具备不确定性,利用空间的叠加来直接更新难以满足要求,需要设置图斑ID、设置插入/修改/删除标识,利用FME的格式属性(fme_db_operation)来实现。

    为避免入库人员的误操作,整体流程需增加重复录入流程终止,录入SDE数据库后支持回退操作。

    规划设计人员习惯使用CAD制图,而普通CAD不具备扩展属性,因此只能使用传统方式规定CAD样式:图斑填充(存在镂空,填充更容易识别)+关键属性(图斑编号,唯一标识码;更新类型,方便fme_db_operation设置插入、修改、删除)。具体样式参见图1。其他属性则使用EXCEl记录(所属镇街、所属类型、基期工业用地比例等),再基于图斑编号进行图属挂接。

                                                                                 图 1 工业保护线入库前CAD样式

    2、技术路线

    基于CAD成果样式,首先需对CAD填充和文本标注进行空间链接,因为文本标注是多行文本,包含2个属性,需对文本标注进行属性整理,整理好的属性就可以结合已入库数据情况对即将入库数据进行是否重复录入或者回退操作判断,以满足规避误操作需求。

    在入库过程中,操作人员难免会存在不经意间重复录入情况,而自己没有发现及时进行回退操作的话,数据库内相同要素会同时存在2份。为了避免此种情况发生,在流程中增加此步骤让机器帮助自动终止,因为回退操作和误入都会存在相同文件输入的过程,故将判断重复录入放在判断是否需要回退之后以避免回退也被终止。

    最后,就可以与EXCEL进行属性挂接,计算更新次数及更新时间并完成入库。

    整个入库流程的具体思路如图2所示,实现的关键是利用FME的FeatureReader获取历史库、实时库内情况进行比对,并根据相应需求提取历史库中要素并设置fme_db_operation字段以完成插入、修改、删除操作达到维护数据库的效果。

                                                                                                   图 2 整体思路流程

    三、具体流程

    1、CAD填充与CAD文本空间链接

    在CAD中,虽然文本标注在CAD填充之上,但文本标注的文本框中心点可能在相邻填充之上,而FME识别文本标注的填充为其文本框中心点。可能存在如图3所示情况:斑块84-L-004的填充上无文本压盖,而斑块4-H-015的填充上有2处文本压盖。

                                                                               图 3 CAD空间链接存在问题

    解决思路为先对填充进行唯一码标识,以文本标注为参考寻找最近的填充,筛选出无异议的进入下一流程,对有冲突的进行空间链接SpatialFilter。具体流程如图4所示。

                                                                               图 4 CAD空间链接具体流程

    2、CAD文本属性整理

    由于输入文本标注为多文本形式,整理文本数据时要注意读模块时要将实体选项中打散多文本实体勾选取消,不取消的话文本将被打散,而且读取顺序会变成随机,text扩展属性列表会存在有些ID在前更新类型在后,有些ID在后更新类型在前情况,不利于后期整理属性。

                                                                   图 5 CAD读模块设置注意取消勾选打散多文本实体

    设置好读模块后只需要使用AttributeSplitter转换器依次对回车符、对冒号进行分割,再利用AttributeCreator新建对应属性并赋值即可,具体流程如图6所示。

                                                                        图 6 CAD文本属性整理及图斑聚合清理

    3、根据用户属性判断是否需要回退

    判断是否回退主要利用Tester转换器,如果是的话执行回退操作。具体操作流程需结合fme_db_operation属性使用,在第7小点再详述,本节重点解决用户参数问题。

    为了让用户在意识到录入错误数据到数据库后能够回退,需要设置用户参数给用户选择,用户参数类型为选择,可选配置为是%否,默认可设置为否,因为毕竟回退是少部分有意识的操作,要注意勾选发布,如此用户在运行转换器是可以调整该参数。Tester转换器再根据此判断是否回退用户参数将处理情况进行分流。

                                                                                       图 7 Tester转换器设置

                                                                                      图 8 用户参数back设置

    4、判断是否重复录入

    判断是否重复录入主要利用FeatureReader转换器,通过读取SDE中工业保护线和一事一议要素(实时库),提取ID和更新类型与CAD录入信息一直要素,并用StatisticsCalculator转换器进行总数统计。当总数大于0说明存在重复录入情况,再利用Tester和Terminator对此种情况进行终止并抛出终止提示。

                                                                            图 9  FeatureReader转换器设置

                                                                              图 10 判断是否重复录入具体流程

    5、CAD与excel挂接

    在CAD与excel挂接之前要先判断更新字段是否为D(删除),因为需要删除的图斑不再有对应的属性,不需要进行挂接。另外,excel中容易存在前后空格,为了便于顺利与CAD图斑的ID顺利挂接,可以使用AttributeTrimmer清除前后空格再进行FeatureMerger。

                                                                               图 11 CAD与excel挂接具体流程

    6、计算更新次数及更新时间

    为了方便数据库管理,实时库内数据需计算更新次数及更新时间。由于CAD与excel挂接流程已经将删除情形分流出去,目前数据流中仅包含I(插入)和UN(更新)两种情形。

    其中,I为首次录入数据库,更新次数直接赋值为0即可,更新时间直接利用FME文本编辑器内的时间函数@DateTimeNow()即可计算出当前时间;UN为图斑更新,目前实时库内存在的相同图斑即为上一次更新次数,只需在此基础上+1即可,而调用实时库内相应图斑也是利用FeatureReader转换器筛选图斑编号一致要素,要注意暴露更新次数属性,再利用FeatureMerger转换器连接回当前更新要素。最后再使用DateTimeConverter转换器规范化时间格式。

                                                                               图 12 FeatureReader转换器设置

                                                                               图 13 计算更新次数及更新时间具体流程

    7、利用fme_db_operation属性更新数据库

    (1)正常入库

    ①实时库

    只需将工业保护线和一事一议两类要素,分别基于更新类型用TestFilter分流,新建fme_db_operation属性赋值对应的insert、update、delete,同时写模块注意设置要素操作为fme_db_operation,同时匹配列为ID(用户属性注意暴露ID)。

                                                                               图 14 正常入库(实时库)流程

                                                                                  图 15 写模块设置

    ②历史库

    历史库需要注意的是里面需要记录实时库每一次变动的过程。首先需要新增字段STATTIME和ENDTIME,还要格外注意即使是删除要素在历史库中也是有保留下来,并要新增特殊标记字段[删除标记],该字段的设置也是方便在回退操作中能找回被删除的要素。

    对于插入要素,入历史库fme_db_operation属性仍为insert,注意新增STARTTIME; 对于更新要素,历史库需包含2次要素调整,1是本次更新要素,2是该要素对应的上一次入库要素,本次更新fme_db_operation属性仍为insert,该要素对应的上一次入库要素需利用FeatureReader读取出来,方法为图斑编号一致且更新次数等于第6节计算的更新次数-1,并暴露所有属性,具体参数设置详见图17,然后记得计算ENDTIME;对于删除要素,因为未参与excel属性挂接,详细属性仍需要读会原始库,故仍使用FeatureReader读取出对应图斑,但读取方法与更新要素不同,因为删除要素没参与到计算更新次数中来,在第5小节CAD与excel挂接中直接被筛选出来参与本流程了,所以需要选择ENDTIME为空且图斑编号一致的要素,暴露所有属性并计算ENDTIME,同时记得新增删除标记且赋值为“是”。

    最后,写模块匹配列仅包含ID已经不具备唯一性了,此时需要再添加STARTTIME。

                                                                             图16 正常入库(历史库)流程

                                                                      图 17 本次更新要素读取的上一次入库要素

                                                                    图 18 本次删除要素读取的上一次入库要素

                                                                                     图 19 写模块设置

    (2)回退操作

    回退操作,其实只涉及新增和删除两类操作。

    ①实时库

    对于上次删除的需要补回,对于上次新增的需要删除,对于上次更新的需要删除实时库中的图斑并找回上上次的对应图斑。

    基于此,可以先使用Tester筛选出删除的图斑,读取历史库中对应图斑补回实时库,由于在历史库入库过程中有专门对删除类要素进行标记,故FeatureReader转换器只需找到ID一致且删除标记为‘是’的图斑,具体设置参见图21。剩余要素通过FeatureReader读出实时库中对应要素,要注意暴露出更新次数(图22),因为这是判断上次操作是更新还是插入的关键字段,而这两种类型需采取不同方式回退。再次使用Tester筛选出更新次数为0的要素(上次新增要素)fme_db_operation直接定义为删除即可,由于一事一议项目仅划定工业保护线时存在,故一事一议项目的写模块回退只需连此一条数据流。此次Tester失败要素为上次更新的要素,需要产生2条数据流,1条增加fme_db_operation赋值delete用来删除实时库中需要回退的要素,另1条用来补回这些要素,只能从历史库中找回,仍是使用FeatureReader读取历史库中ID一致且更新次数 = @Value(更新次数)-1的要素,fme_db_operation赋值insert。完整流程如图20所示。

                                                                                图 20 回退操作(实时库)流程

    图 21 读取需回退的删除要素

     

                                                          图 22 读取上次操作非删除类要素FeatureReader转化器设置

    ②历史库

    历史库回退操作流程与实时库基本一致,主要是针对实时库中fme_db_operation赋值为insert的要素更新并对删除标记、ENDTIME字段进行处理。

    对于上次删除的需要更新,主要操作是将删除标记、ENDTIME清零;对于上次新增的需要删除;对于上次更新的需要删除历史库中的图斑并将上上次的对应图斑ENDTIME清零。

                                                                           图 23 回退操作(历史库)流程

    8、输出数据属性设置

    对于输出数据库属性类型可以在用户属性处进行设置,主要更新时间、STARTTIME、ENDTIME设置为时间格式,更新次数设置为整数型等,具体可参考图24。尤其注意STARTTIME做为历史库行选择集的匹配列一定要输出规范,不然可能会导致更新出错。

                                                                                           图 24 数据属性设置

    四、结语

    在整个模板FME全程零代码操作,仅涉及简单的几句SQL语言以实现对数据库内的数据提取。可以让流程设计者倾注更多的心力关注流程本身,而非编程语言中各种对象的定义。以一种非常高效的流程可视化编程方式实现一些复杂功能。

    结合FMESERVER的文件检测和收发邮件功能,后期还可进一步优化流程,指定特定文件夹作为数据库入库文件夹,发布Directory Watch 协议监控文件的创建和删除,发布到特定主题,主题检测到创建文件执行入库命令,检测到删除文件使用Tester将判断是否回退用户参数设为是,执行回退命令。而每一次Job Submitter之后自动发送邮件给用户,基本实现全程自动化操作。

     

    展开全文
  • 五、实战:当当网5图书排行榜数据抓取 目标站点 当当5图书排行榜 抓取5好评图书数据: items=//ul[@class='bang_list clearfix bang_list_mode']/li #所有图书条目,返回列表 for item in items: item.xpath(...

    Python开发简单爬虫速成教程

    b站学习视频网址

    https://www.bilibili.com/video/BV1164y1m7Xb?p=1

    一、简介

    按照一定的规则,自动抓取网络信息的程序或者脚本,搜索引擎的重要组成。

    • 搜索引擎

    • 数据分析

    • 人工智能

    • 薅羊毛、抢车票

    爬虫产品:

    神箭手、八爪鱼、造数、后羿采集器

    数据储备:

    • python编程基础
    • http
    • 数据库

    怎么学:

    • python语法知识

    • python爬虫常用到的几个重要内置库Requests,用于请求网页

    • 学习正则表达式re、Xpath(IxmI)等网页解析工具

    • 开始一些简单的网站爬取,了解爬取数据的过程(当当网图书数据)

    • 与数据库结合,将爬取数据进行存储

    安装MongoDB数据库

    • 介于关系型数据库和非关系型数据库之间的产品
    • 本质是非关系型数据库,最像关系数据库
    • 支持的数据结构非常松散,类似json的bson格式
    • 可以存储比较复杂的数据

    非关系模型:

    • 列模型,一列为一个记录,分布式数据库,HBase
    • 键值对,redis
    • 文档类,类似键值对,Mongodb

    效率非常高,存储在内存中,断电即失,现在也可存储到磁盘中

    检查安装是否成功:

    image-20210724091237530

    修改设置:

    /bin/mongod.cfg

    image-20210724091652305

    右击我的电脑,管理,服务和应用程序,服务,找到MongoDB,点击重新启动。同一个局域网的机器可以连接。

    安装Navicat:支持多种数据库的连接

    安装教程:Navicat安装教程

    使用教程:在 Navicat for MongoDB 使用文档

    使用Navicat,进行MongoDB数据库的连接:

    注意主机和端口号的设置

    二、request模块入门

    requests库:

    通过Requests库发送http请求:

    image-20210724101305407

    Requests模块的安装:

    cmd直接安装:pip install requests

    Requests模块支持的http方法:

    • Get方法:请求资源

    • Post方法:向服务端发送信息和数据,表单提交

    • Head方法:请求资源的一些信息而不是全部信息,用于确认URL的有效性以及资源更新的日期时间

    • Put方法:上传文档

    • Connection方法:客户端想要确定一个明确的连接到远程主机,便于通过Http代理服务器进行SSL加密通信时连接使用,在抓包时使用

    【重点】通过requests模块发送get请求

    import requests
    r=requests.get('http://httpbin.org/ip') #会返回当前主机的一个外网地址
    # r1=requests.get('https://www.imooc.com') 
    print(r.text) # 输出请求体
    

    cmd查看内网ip:ipconfig

    百度查看ip地址:ip地址

    【重点】通过requests模块发送post请求

    一般出现在html的form表单里面

    import requests
    
    r2=requests.post(url='http://httpbin.org/post',data={'name':'imooc'})
    print(r2.text)
    

    通过requests模块的get请求构造url

    主要用在get请求里面

    image-20210724110613219

    data={"key1":"value1","key2":"value2"}
    response=requests.get("http://httpbin.org/get",params=data)
    # 查看当前请求的url
    print(response.url)
    

    查看响应的返回头和返回体

    # 查看响应的返回头
    print(response.headers)
    # 查看响应的返回体
    print(response.text)
    

    返回头:

    image-20210724140012986

    【重点】请求图片

    url='https://www.imooc.com/static/img/index/logo2020.png'
    r1=requests.get(url)
    # 访问图片返回的是二进制数据,需要用content查看输出
    print(r1.content) 
    
    #写入图片数据
    with open("imooc.png","wb") as f:
        f.write(r1.content)
    

    图片二进制数据:

    image-20210724140620310

    写入图片:

    image-20210724140646990

    查看响应的json数据

    r1=requests.get(url="http://httpbin.org/ip")
    print(r1.json()) # 整个json对象 {'origin': '124.160.215.146'}
    
    print(r1.json()["origin"]) # json中某一项 124.160.215.146
    

    查看响应状态码

    print(r1.status_code)
    

    查看request的请求头

    print(r1.request.headers)
    

    image-20210724141149355

    请求头可以修改,可以通过构造request请求头

    headers={
        "user-agent":"joseph/v1"
    }
    r1=requests.get(url="http://httpbin.org/ip",headers=headers)
    print(r1.request.headers)
    

    image-20210724141331591

    在反爬机制中可以使用,可以模拟win,ipad,手机

    三、requests模块进阶

    设置连接超时

    当请求超时,放弃请求

    # 0.0001秒之内返回网页数据
    # 超时时间一般设置为2到3秒
    start_time=time.time()
    r1=requests.get(url="https://www.imooc.com",timeout=2)
    end_time=time.time()
    
    print(r1.text)
    print(end_time-start_time)
    

    查看响应设置的cookies

    可以在headers中查看

    也可以通过cookies查看

    行为和字典非常的像

    r1=requests.get(url="https://www.baidu.com")
    print(r1.headers)
    print(r1.cookies)
    print(r1.cookies["BDORZ"])
    

    image-20210724143511229

    在请求中携带cookies

    cookies={
        "username":"joseph"
    }
    r1=requests.get(url="http://httpbin.org/cookies",cookies=cookies)
    print(r1.cookies) # 输出响应设置的cookies
    print(r1.text) # 请求url在响应中返回请求的cookies 
    

    image-20210724144021399

    证书校验,SSLError问题

    request可以为HTTPS请求验证SSL证书,就像web浏览器一样

    SSL验证默认是开启的,如果证书验证失败,requests会抛出SSLError

    # 关闭SSL校验
    r1=requests.get(url="https://www.baina.org",verify=False)
    # 方法二:指定网站的SSL证书,但是一般没有
    print(r1.text)
    

    异常

    • 网络问题:如DNS查询失败,拒绝连接,会抛出ConnectionError异常

    • Http请求返回了不成功的状态码:HTTPError异常

    • 请求超时:Timeout异常

    • 请求次数超过了设定的最大重定向次数,TooManyRedirects异常

    • 所有request显式抛出的异常都继承自requests.exceptions.RequestException

    【重点】保持登录凭据,requests.session(),爬取需登录的网站+定制请求头

    http://account.chinaunix.net/login进行登录验证

    1.通过get请求访问登录页面,这个时候还没有填写username和password

    image-20210724152216258

    image-20210724152253422

    image-20210724152403350

    2.post请求访问登录

    第二个使用的token信息就是第一个请求setCookies设置的

    时间戳,删除后3位

    image-20210724152527355

    image-20210724152631963

    image-20210724152701794

    image-20210724152852463

    3.index页面

    cookies携带了很多数据,

    可以通过定制请求头和设置请求的cookies 进入index页面,可行但太麻烦。

    image-20210724152945906

    image-20210724153116022

    image-20210724153213188

    保持登录凭据,从而直接绕过登录,直接进入到index页面:

    • get请求拿token信息

    • post请求提交用户名密码

    • 第三个请求直接访问登录后index页面

    定制请求头header

    请求头替换添加引号:

    采用正则表达式

    image-20210724153927872

    代码实现,利用requests.session()保存登录凭据

    # request.session,创建一个保持登录凭据的session实例
    login_session=requests.session()
    
    
    # 1、get请求,获取token信息
    token_url="http://account.chinaunix.net/login?"
    headers={
        "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        "Accept-Encoding":"gzip, deflate",
        "Accept-Language":"zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
        "Cache-Control":"no-cache",
        "Host":"account.chinaunix.net",
        "Pragma":"no-cache",
        "Proxy-Connection":"keep-alive",
        "Upgrade-Insecure-Requests":"1",
        "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36 Edg/91.0.864.71"
    }
    # 发送第一个get请求
    token_response=login_session.get(url=token_url,headers=headers)
    # print(token_response.headers.get("Set-Cookie"))
    # 通过正则表达式拿到token值,需要导入import re
    token_search=re.compile(r"XSRF-TOKEN=(.*?);")
    token_value=token_search.search(token_response.headers.get("Set-Cookie"))
    print(token_value.group(1))
    
    
    
    # 2.post请求
    # 时间戳导入 import time模块生成
    login_url="http://account.chinaunix.net/login/login"
    data={
        "username": "dazhuang_imooc",
        "password": "abcd1234",
        "_token": "QPABdRUD0iujCY7GCgv4RARvOlUBcQY39e3vSaxu",
        "_t": int(time.time())
    }
    # 发送post请求,提交用户名和密码,注意不要忘记携带data
    login_response=login_session.post(url=login_url,headers=headers,data=data)
    print(login_response.text)
    
    
    
    # 3.访问index页面
    index_url="http://account.chinaunix.net/ucenter/user/index"
    # 【重要】一定要使用login_session,否则登录凭据是不携带的
    index_response=login_session.get(url=index_url)
    print(index_response.text)
    

    【重点】设置代理,隐藏请求

    服务端可以看到客户端的请求地址,我们可以通过设置代理来隐藏我们自己

    快代理:

    • 可以提供隧道代理

    • 本机连接代理的两种形式:每个请求携带用户名和密码,设置白名单类似ssh

    # 查看本机的ip和城市
    url="http://pv.sohu.com/cityjson"
    response=requests.get(url=url)
    print(response.text)
    
    # 1.挑选代理厂商:阿布云,芝麻代理,快代理
    # 2.把代理信息设置到代码中,两种格式:http和https
    url="http://pv.sohu.com/cityjson"
    
    # 免费代理,无需用户名和密码
    proxies1={
        "http":"http://117.157.197.18:3128",
        "https":"https://117.157.197.18:3128",
    }
    
    # 付费代理:格式需要注意,定义一个字典http和https
    # 用户名:密码@代理的接口:端口号
    proxies={
        "http":"http://t10422119804320:oynsb8dh@tps194.kdlapi.com:15818",
        "https":"https://t10422119804320:oynsb8dh@tps194.kdlapi.com:15818",
    }
    
    # 发送请求携带代理信息
    for i in range(5):
        response=requests.get(url=url,proxies=proxies)
        print(response.text)
    

    四、Xpath基础和lxml模块

    XPath知识点

    html节点与节点间的关系,Xpath和lxml获取想要的数据字段

    节点有哪些?

    image-20210724194334642

    先辈节点不唯一,后辈节点也不唯一

    image-20210724194630724

    学习XPath需要安装插件:XPath helper

    image-20210724195157608

    xpath路径表达式:

    汪曾祺《黄油烙饼》

    /div 单斜杆定位根节点下div节点

    //div 双斜杠定位文档中所有div节点

    //div[@class='pic_art'] 获取特定元素 网页元素

    //div[@class='pic_art']/text() 获取特定元素 整个文本信息

    //div[@class='pic_art']/. 一个点表示当前节点

    //div[@class='pic_art']/.. 两个点表示获取当前节点的父节点

    /div/a 从根节点开始选取div节点下的a节点

    //div[@class='info']/span[@class='time']

    /div/a[2]/img 从根节点开始选取div节点下的第二个a节点下的img节点

    //div[@class=‘header-wrapper’] 选取所有属性class的值为header-wrapper的div节点

    • 在节点后加上带有数字的方括号,兄弟节点索引/div/a[2]/img

    • 在节点后加上属性名-值对的方括号,可以筛选出需要的节点//div[@class='info']/span[@class='time']

    • //* 选取所有元素

    • //@* 选取所有带属性的元素

    xpath函数:

    div/p/text() 选取文本内容

    模糊查询:

    //div[contains(@class,”post”)] 选取带有class属性且包含“post”的所有div节点。取反//div[ not(contains(@class,”post”))]

    /div/p[last()-1] 选取div下倒数第二个p节点

    div/p[position()>1] 选取div下第二个p节点后的所有兄弟节点

    /div/a|div/p 选取div下的a节点和div下的p节点,竖线分隔or

    Python lxml模块

    可以灵活处理xml

    支持xpath

    利用XPath语法,快速定位特定元素以及节点信息,以提取出html或xml目标数据

    安装lxml库:

    在cmd中输入pip install lxml

    实操:通过xpath获取想要的数据字段

    from lxml import etree
    # 不包含html标签和body标签
    data="""
    <div>
        <ul>
            <li class="item-0"><a href="link1.html">first item</a></li>
            <li class="item-1"><a href="link2.html">second item</a></li>
            <li class="item-inactive"><a href="link3.html"><span class="bold">third item</span></a></li>
            <li class="item-1"><a href="link4.html">fourth item</a></li>
            <li class="item-0"><a href="link5.html">fifth item</a></li>
        </ul>
    </div>
    """
    #html实例化,添加了html和body标签
    html=etree.HTML(data) 
    print(etree.tostring(html).decode()) 
    
    
    print(html.xpath("//li"))
    print(html.xpath("//li/@class"))
    
    print(html.xpath("//li/a[@href='link1.html']"))
    print(html.xpath("//li[1]/a[1]"))
    print(html.xpath("//li[last()]/a[1]"))
    
    print(html.xpath("//li/a/span"))
    print(html.xpath("//li//span")) 
    # 输出的所有结果都是列表的形式
    

    五、实战:当当网5星图书排行榜数据抓取

    目标站点

    当当5星图书排行榜

    抓取5星好评图书数据:

    image-20210724210845210
    items=//ul[@class='bang_list clearfix bang_list_mode']/li #所有图书条目,返回列表
    
    for item in items:
        item.xpath(".//div[@class='name']/a/text()") #图书的标题
        item.xpath(".//div[@class='star']/a/text()") #图书的评论
        item.xpath(".//span[@class='tuijian']/text()") #图书的推荐信息
        item.xpath(".//div[@class='publisher_info'][1]/a/@title") #图书的作者
        item.xpath(".//div[@class='publisher_info'][2]/span/text()") #图书的出版时间
        
        item.xpath(".//div[@class='publisher_info'][2]/a/text()") #出版社信息
        item.xpath(".//div[@class='biaosheng']/span/text()") #评分信息
        item.xpath(".//div[@class='price']/p[1]/span[1]/text()") #图书价格
        item.xpath(".//div[@class='price']/p/span[@class='price_s']/text()") #折扣信息
        item.xpath(".//div[@class='price']/p[@class='price_e']/span/text()") #电子书价格  
    

    构造请求头headers:浏览器复制请求头,利用notepad++正则表达式格式化,加引号

    【易错】需要去除复制的header里面的空格

    image-20210724213902470

    爬取图书数据,存储为列表形式

    import requests
    from lxml import etree
    
    class Dangdang(object):
        def __init__(self):
            """构造方法"""
            self.header={
                "Host":"bang.dangdang.com",
                "Proxy-Connection":"keep-alive",
                "Pragma":"no-cache",
                "Cache-Control":"no-cache",
                "Upgrade-Insecure-Requests":"1",
                "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36 Edg/91.0.864.71",
                "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
                "Accept-Encoding":"gzip, deflate",
                "Accept-Language":"zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"
            }
    
            
        def get_dangdang(self,page):
            """发送请求到当当网获取数据"""
            url="http://bang.dangdang.com/books/fivestars/01.00.00.00.00.00-recent30-0-0-1-%s" % page
            # 发送请求
            response=requests.get(url=url,headers=self.header) # 1.response获取一个页面的网页内容
            if response:
                # 2.HTML数据的实例化。通过xpath表达式抽取网页数据,获取各个图书的网页数据,以列表形式返回
                html=etree.HTML(response.text)
                items=html.xpath("//ul[@class='bang_list clearfix bang_list_mode']/li")
                return items
    
            
        # xpath获取的数据是一个列表,转换为字符串
        def join_list(self,item):
            """处理列表到字符串"""
            return "".join(item)
    
        
        def parse_item(self,items):
            """解析具体的图书条目"""
            # 定义一个列表,用于存放存储到MongoDB之前的数据
            result_list=[]
    		
            # 1.对抽取的每一个图书网页数据,进行进一步数据抽取,获得每本图书的各项具体信息节点
            for item in items: 
                title=item.xpath(".//div[@class='name']/a/text()") #图书的标题
                comment=item.xpath(".//div[@class='star']/a/text()") #图书的评论
                recommend=item.xpath(".//span[@class='tuijian']/text()") #图书的推荐信息
                author=item.xpath(".//div[@class='publisher_info'][1]/a/@title") #图书的作者
                publication_time=item.xpath(".//div[@class='publisher_info'][2]/span/text()") #图书的出版时间            
                press=item.xpath(".//div[@class='publisher_info'][2]/a/text()") #出版社信息
                score=item.xpath(".//div[@class='biaosheng']/span/text()") #评分信息
                price=item.xpath(".//div[@class='price']/p[1]/span[1]/text()") #图书价格
                discount=item.xpath(".//div[@class='price']/p/span[@class='price_s']/text()") #折扣信息
                e_book=item.xpath(".//div[@class='price']/p[@class='price_e']/span/text()") #电子书价格 
    			
                # 2.每本图书的各项具体信息都是以列表存储节点的形式展现,将列表变为字符串。再将每个节点信息封装成一个JSON对象,存储到结果列表result_list中
                result_list.append(
                    {
                        "title":self.join_list(title),
                        "comment":self.join_list(comment),
                        "recommend":self.join_list(recommend),
                        "author":self.join_list(author),
                        "publication_time":self.join_list(publication_time),
                        "press":self.join_list(press),
                        "score":self.join_list(score),
                        "price":self.join_list(price),
                        "discount":self.join_list(discount),
                        "e_book":self.join_list(e_book)
                    }
            )
            return result_list
    
        
    
    def main():
        import json
        d=Dangdang()
        for page in range(1,26):
            items=d.get_dangdang(page=page)
            result=d.parse_item(items=items)
            print(json.dumps(result)) #复制一条,浏览器打开json.cn可以看到json形式
            
            
            
    if __name__ == '__main__':
        main()
    

    image-20210724221647539

    python操作MongoDB

    安装pymongo:

    pip install pymongo

    python中连接mongodb数据库+插入数据

    import pymongo
    
    # 指定一个客户端连接Mongodb
    pymongo_client=pymongo.MongoClient("mongodb://127.0.0.1:27017")
    # 指定数据库的名称imooc
    # 只有库里面由内容插入的时候,才会创建数据库
    pymongbo_db=pymongo_client["imooc"]
    # 创建集合(数据库表)
    pymongo_collection=pymongbo_db["pymongo_test"]
    data={
        "name":"imooc",
        "flag":1,
        "url":"https://www.baidu.com"
    }
    # 插入一条数据
    pymongo_collection.insert_one(data)
    
    # 插入多条数据,列表
    mylist=[
        {"name":"taobao","flag":"100","url":"https://www.taobao.com"},
        {"name":"QQ","flag":"101","url":"https://www.qq.com"},
        {"name":"facebook","flag":"102","url":"https://www.facebook.com"},
        {"name":"zhihu","flag":"103","url":"https://www.zhihu.com"},
        {"name":"github","flag":"104","url":"https://www.github.com"},
    ]
    pymongo_collection.insert_many(mylist)
    

    image-20210724223554139

    数据查询:

    # 查询
    # 第一个{}表示进行数据的查询,第二个{}进行过滤,find是查询所有数据
    result=pymongo_collection.find({},{"_id":0,"name":1,"flag":1}) #结果是一个游标<pymongo.cursor.Cursor object at 0x00000201BE84EEB0>,可以通过for循环进行遍历
    for item in result:
        print(item)
    
    # 查询一条数据
    result2=pymongo_collection.find_one() 
    print(result2)
    
    # 高级查询
    # result3=pymongo_collection.find({"flag":{"$gt":"100"}}) # 查询结果flag都大于100,大小输出
    result3=pymongo_collection.find({"name":{"$regex":"^g"}}) # 正则表达式,限定输出
    for item in result3:
        print(item)
    

    image-20210724224909558

    数据更新:

    # 数据更新
    # 更新一条(第一个参数是查询条件,第二个参数是要修改成为什么样子)
    pymongo_collection.update_one({"name":{"$regex":"^g"}},{"$set":{"name":"gitee"}})
    
    result=pymongo_collection.find({},{"_id":0,"name":1,"flag":1}) 
    for item in result:
        print(item)
    
    # 更新多条
    pymongo_collection.update_many({},{"$set":{"name":"gitee"}})
    
    result=pymongo_collection.find({},{"_id":0,"name":1,"flag":1})
    for item in result:
        print(item)
    

    数据删除:

    # 数据删除
    # 删除一条
    pymongo_collection.delete_one({"url":"https://www.baidu.com"})
    # 删除所有数据
    pymongo_collection.delete_many({})
    

    将爬取的当当网图书数据存储到mongdb中

    from typing import Collection
    from pymongo import collection, mongo_client
    import requests
    from lxml import etree
    import pymongo
    from pymongo.collection import Collection # Collection不要写错
    
    class Dangdang(object):
        # ******************************************************************************************
        # 连接数据库
        mongo_client=pymongo.MongoClient(host="127.0.0.1",port=27017)
        # 指定数据库为dangdang_db
        dangdang_db=mongo_client["dangdang_db"]
        # ******************************************************************************************
    
        def __init__(self):
            self.header={
                "Host":"bang.dangdang.com",
                "Proxy-Connection":"keep-alive",
                "Pragma":"no-cache",
                "Cache-Control":"no-cache",
                "Upgrade-Insecure-Requests":"1",
                "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36 Edg/91.0.864.71",
                "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
                "Accept-Encoding":"gzip, deflate",
                "Accept-Language":"zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"
            }
            # ******************************************************************************************
            # 构造方法创建Collection
            self.dangdang=Collection(Dangdang.dangdang_db,"dangdang") # 表名为dangdang
            # ******************************************************************************************
    
        def get_dangdang(self,page):
            """发送请求到当当网获取数据"""
            url="http://bang.dangdang.com/books/fivestars/01.00.00.00.00.00-recent30-0-0-1-%s" % page
            print(url)
            # 发送请求
            response=requests.get(url=url,headers=self.header)
            if response:
                # HTML数据的实例化
                html=etree.HTML(response.text)
                items=html.xpath("//ul[@class='bang_list clearfix bang_list_mode']/li")
                return items
    
        # xpath获取的数据是一个列表,转换为字符串
        def join_list(self,item):
            """处理列表到字符串"""
            return "".join(item)
    
        def parse_item(self,items):
            """解析具体的图书条目"""
            # 定义一个列表,用于存放存储到MongoDB之前的数据的
            result_list=[]
    
            for item in items: # 一个items包括25条数目,即最终一个result_list包含25条数据每次
                title=item.xpath(".//div[@class='name']/a/text()") #图书的标题
                comment=item.xpath(".//div[@class='star']/a/text()") #图书的评论
                recommend=item.xpath(".//span[@class='tuijian']/text()") #图书的推荐信息
                author=item.xpath(".//div[@class='publisher_info'][1]/a/@title") #图书的作者
                publication_time=item.xpath(".//div[@class='publisher_info'][2]/span/text()") #图书的出版时间            
                press=item.xpath(".//div[@class='publisher_info'][2]/a/text()") #出版社信息
                score=item.xpath(".//div[@class='biaosheng']/span/text()") #评分信息
                price=item.xpath(".//div[@class='price']/p[1]/span[1]/text()") #图书价格
                discount=item.xpath(".//div[@class='price']/p/span[@class='price_s']/text()") #折扣信息
                e_book=item.xpath(".//div[@class='price']/p[@class='price_e']/span/text()") #电子书价格 
    
                result_list.append(
                    {
                        "title":self.join_list(title),
                        "comment":self.join_list(comment),
                        "recommend":self.join_list(recommend),
                        "author":self.join_list(author),
                        "publication_time":self.join_list(publication_time),
                        "press":self.join_list(press),
                        "score":self.join_list(score),
                        "price":self.join_list(price),
                        "discount":self.join_list(discount),
                        "e_book":self.join_list(e_book)
                    }
                )
    
            return result_list
    	
        # ******************************************************************************************
        def insert_data(self,result_list):
            """插入数据到mongodb"""
            self.dangdang.insert_many(result_list) #一次20条数目
        # ******************************************************************************************
    
    def main():
        import json
        d=Dangdang()
        for page in range(1,26):
            items=d.get_dangdang(page=page)
            result=d.parse_item(items=items)
            # print(len(result)) # 浏览器每个页面输出记录条数
            # print(json.dumps(result)) #复制一条,浏览器打开json.cn可以看到json形式
            d.insert_data(result_list=result)        
    
    
    if __name__ == '__main__':
        main()
    

    爬虫的伪装:使用代理

    from typing import Collection
    from pymongo import collection, mongo_client
    import requests
    from lxml import etree
    import pymongo
    from pymongo.collection import Collection
    
    class Dangdang(object):
        # 连接数据库
        mongo_client=pymongo.MongoClient(host="127.0.0.1",port=27017)
        # 指定数据库为dangdang_db
        dangdang_db=mongo_client["dangdang_db"]
    
        def __init__(self):
            self.header={
                "Host":"bang.dangdang.com",
                "Proxy-Connection":"keep-alive",
                "Pragma":"no-cache",
                "Cache-Control":"no-cache",
                "Upgrade-Insecure-Requests":"1",
                "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36 Edg/91.0.864.71",
                "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
                "Accept-Encoding":"gzip, deflate",
                "Accept-Language":"zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"
            }
            # 构造方法创建Collection
            self.dangdang=Collection(Dangdang.dangdang_db,"dangdang") # 表名为dangdang
            
            # ******************************************************************************************
            #使用代理,在request方法中加上代理信息
            self.proxies={
                "http":"http://t10422119804320:oynsb8dh@tps194.kdlapi.com:15818",
                "https":"https://t10422119804320:oynsb8dh@tps194.kdlapi.com:15818",
            }
            # ******************************************************************************************
    
    
        def get_dangdang(self,page):
            """发送请求到当当网获取数据"""
            url="http://bang.dangdang.com/books/fivestars/01.00.00.00.00.00-recent30-0-0-1-%s" % page
            print(url)
            # ******************************************************************************************
            # 发送请求,此时爬虫项目使用了代理,进行爬取数据的
            response=requests.get(url=url,headers=self.header,proxies=self.proxies)
            # ******************************************************************************************
            if response:
                # HTML数据的实例化
                html=etree.HTML(response.text)
                items=html.xpath("//ul[@class='bang_list clearfix bang_list_mode']/li")
                return items
    
        # xpath获取的数据是一个列表,转换为字符串
        def join_list(self,item):
            """处理列表到字符串"""
            return "".join(item)
    
        def parse_item(self,items):
            """解析具体的图书条目"""
            # 定义一个列表,用于存放存储到MongoDB之前的数据的
            result_list=[]
    
            for item in items: # 一个items包括25条数目,即最终一个result_list包含25条数据每次
                title=item.xpath(".//div[@class='name']/a/text()") #图书的标题
                comment=item.xpath(".//div[@class='star']/a/text()") #图书的评论
                recommend=item.xpath(".//span[@class='tuijian']/text()") #图书的推荐信息
                author=item.xpath(".//div[@class='publisher_info'][1]/a/@title") #图书的作者
                publication_time=item.xpath(".//div[@class='publisher_info'][2]/span/text()") #图书的出版时间            
                press=item.xpath(".//div[@class='publisher_info'][2]/a/text()") #出版社信息
                score=item.xpath(".//div[@class='biaosheng']/span/text()") #评分信息
                price=item.xpath(".//div[@class='price']/p[1]/span[1]/text()") #图书价格
                discount=item.xpath(".//div[@class='price']/p/span[@class='price_s']/text()") #折扣信息
                e_book=item.xpath(".//div[@class='price']/p[@class='price_e']/span/text()") #电子书价格 
    
                result_list.append(
                    {
                        "title":self.join_list(title),
                        "comment":self.join_list(comment),
                        "recommend":self.join_list(recommend),
                        "author":self.join_list(author),
                        "publication_time":self.join_list(publication_time),
                        "press":self.join_list(press),
                        "score":self.join_list(score),
                        "price":self.join_list(price),
                        "discount":self.join_list(discount),
                        "e_book":self.join_list(e_book)
                    }
                )
    
            return result_list
    
        def insert_data(self,result_list):
            """插入数据到mongodb"""
            self.dangdang.insert_many(result_list) #一次25条数目
    
    def main():
        import json
        d=Dangdang()
        for page in range(1,26):
            items=d.get_dangdang(page=page)
            result=d.parse_item(items=items)
            # print(len(result)) # 浏览器每个页面输出记录条数
            # print(json.dumps(result)) #复制一条,浏览器打开json.cn可以看到json形式
            d.insert_data(result_list=result)
    
            
    
    
    if __name__ == '__main__':
        main()
    

    使用了代理以后,抓取的数据会变慢很多

    小结

    • 开发环境的配置

      • 了解了关系型数据库和非关系型数据库
      • 安装了MongoDB
      • 安装使用navicat可视化工具,连接了MongoDB数据库
    • 爬虫基础

      • 学习了http和https相关知识
      • 浏览器中观察到了http请求头,以及返回数据,状态码,get请求,post请求
    • request模块——获取网页内容,通过request模块可以请求到网页数据了

      • requests基础模块学习了发送get请求、post请求

      • requests进阶模块中,学习了如何定制请求头、如何获取获取cookie值,如何加载证书,如何保存登录凭据、如何携带代理

    • XPath语句和lxml模块——抽取网页数据,实例化html数据,通过xpath语句获取想要的网页数据

      • 通过XPath语句,在浏览器中使用xpath helper工具来获取网页数据
      • 通过lxml模块,去实例化html数据,并且通过xpath语句,在代码中或许想要的网页数据
    • 爬虫实战——当当网5星排行榜图书数据爬取+将数据保存到mongodb数据库

      • python操作mongodb数据库
      • 项目实战爬取
    展开全文
  • 坐标准确度问题:卫星定位最少要求4颗同时观测(由于其三球定位原理,再加上把时间作为变量,所以至少需要4颗来观测),而在不少情况下,移动设备并不能满足同时接收到4颗以上卫星的信号(比如:室内、高层建筑...

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/

    此篇为跨度两年的轨迹系列文章总结之一。

    1.轨迹,为什么需要“装扮”

           得益于移动设备的普及性,在缺少其他监管手段的情况下,城市管理中基于移动设备实现对人员、车辆的管理是十分行之有效的方法。其中轨迹的管理方案囊括了轨迹里程统计、工作状态监管、实时位置查看、历史轨迹回溯等等,而这一切方案能够真正的行之有效则必须依赖于一条足够“优美”的轨迹线。只有轨迹线“优美”,统计才会更精确、展示才会更直观。

          

           但是想把一条轨迹线处理到足够优美,并不是一件容易的事情,它受到太多太多的外部影响。这里,我直接列出影响轨迹展示效果的主要因子,希望大家能够带着这些问题一起探讨解决思路,了解原来一条优美的轨迹线后面有这么多的故事:

           a.坐标采集频率问题:轨迹线是由轨迹点连接组成,而轨迹点一定都是离散的,离散程度取决于坐标获取的频率。频率高,设备耗电,轨迹存储增大,但是轨迹特征明显;频率低,轨迹线特征容易丢失。如何能够比较智能的调整坐标上报频率?

           b.坐标采集保活问题:移动设备普遍具有省电模式、息屏功能等,这些场景均容易导致GPS采集程序被系统清除,从而导致坐标在某段时间突然无法采集、信号中断。而互联网APP却往往早已被设备添加至白名单(与厂商有合作关系等),并不会有此问题。

           c.轨迹坐标转换问题:GPS是WGS84坐标,北斗是CGCS2000坐标,而轨迹展示依托的地图往往是各种不同坐标系的地图,两者直接需要进行坐标转换。而且不同于互联网应用只采用一种地图,只需解决一种坐标类型的转换,城市管理中需要解决的坐标转换具有地图类型多、地图精度不可控(如脱密地图)等问题。而坐标转换的准确度将直接影响轨迹展示的效果。

           d.坐标准确度问题:卫星定位最少要求4颗星同时观测(由于其三球定位原理,再加上把时间作为变量,所以至少需要4颗星来观测),而在不少情况下,移动设备并不能满足同时接收到4颗以上卫星的信号(比如:室内、高层建筑遮挡等),这将导致此时的卫星定位误差很大。如何可以提高卫星定位的准确度?

           e.轨迹坐标去噪问题:坐标准确度问题是一个不可避免的问题,在此情况下,需要一套可以对坐标异常点进行去除的方法,从而保证轨迹展示的效果不受异常点的影响。

           f.轨迹线连接问题:轨迹线的逐点连接必然会导致线和线之间过渡不平滑,并且单纯的轨迹点直连,还容易出现轨迹线“穿墙”问题(A点和B点分别在十字路口不同地方,AB直连将导致线条穿越中间的非路面建筑)。

           g.轨迹播放问题:轨迹天然是动态的,仅仅以轨迹线描述不能真实展现轨迹的时空特征。如何优美的展示轨迹动态,让用户具有看动画片一样的观感?

       

    2.优化,从坐标采集开始

             在上述的各种轨迹展示影响因子中,GPS采集频率、GPS采集保活、GPS准确度优化均属于GPS获取环节需解决的问题,并且是影响权重最大的问题,以下对此三点的解决做一个简单的描述。

    2.1坐标采集频率优化

            

           如前文所述,坐标采集频率直接关系到轨迹点的密度,理论上轨迹点是越密越好,但是实际中要考虑设备的耗电量、存储的大小和有效值。举如下几个例子,可以更好的理解轨迹采集频率的调整策略:

           a.某监督员在一条笔直的道路上缓行,此时轨迹每隔20秒采一个轨迹点和每隔60秒采一个轨迹点的效果是一样的,并不会对轨迹的展示有很大的影响。

           b.某监督员进入室内办公,此时的场景与笔直路上缓行是一样的。

           c.某监督员缓行时,突然开始跑步,此时轨迹点每隔20秒与每隔60秒采集将有很大区别,因为此时间段内的直线距离会有较大区别,容易出现特征点遗漏。

           基于以上三种情形,坐标采集频率的自动调整方案便可以做一个简单的归纳:

           a.系统有设置的默认采集频率,针对人员和车辆是不同的。

           b.当连续多个采集点为静止或相差不大时(在阈值范围内),此时可以将坐标采集频率适当调慢。

           c.当连续多个采集点的距离大于阈值范围时,此时可以将坐标采集频率适当调快。

    2.2坐标采集程序保活优化

           我们首先以研究“咕咚运动”的保活机制作为例子来说明:

           (1)当"咕咚"处于停止状态时,一键清理和黑屏状态会被杀死,说明在没有进入运动界面之前,其保活机制没有被启动(即没有使运动界面切换到后台等)。

           (2)当“咕咚”处于运动状态时,一键清理和黑屏状态没有被杀死(滑动清理除外),说明已经启动保活机制:

           a."咕咚"禁止了返回键,以保证运动Activity不被销毁;

           b.不断更新通知栏计时,以保证APP始终在前台,防止被系统回收;

           c."咕咚"被清理后能够自动重启,通知被删除后自动弹出,说明可能有另外一个东西(进程或Service)监听器运动Service(或进程)存活状态,当Service被销毁时,立马将其拉起来;

           d.“咕咚”被强制停止或清理杀死后,再次进入会直接显示运动界面且能够保持杀死之前的运动状态,说明其可能利用配置文件记录了相关状态;

           e.锁屏/解锁后,"咕咚"运动界面会自动弹出,说明其利用了广播机制对锁屏广播进行监听,弹出Activity以保证进程始终在前台;
           总结“咕咚运动”的保活机制为:常驻通知栏,双进程守护,广播锁屏,自定义锁屏。

           同样,我们也分别研究了“悦动力”、“悦动圈”这类运动APP,其保活机制大同小异,所以最终我们将坐标采集程序保活策略以如上分析进行了相关优化。

    2.3坐标准确度优化

           坐标信号强度对定位准确度影响十分大,比如当设备在室内或者高楼下时,坐标信号是明显减弱的。此时我们采用了如下优化策略:

           a.记录信号强弱作为后续轨迹奇异点(问题点)的筛选条件之一。

           b.当信号弱的超过一定阈值时,切换成百度定位方案。

    3.拟合,让轨迹点更有质量

           说完坐标采集的系列优化方案,我们接下来一起探讨从算法层面上对轨迹点进行优化的策略。依据实施条件不同,展示要求不同,这里我们先后从三个不同方向进行轨迹点拟合研究。

    3.1 基于道路线的路网匹配拟合

             顾名思义,本方案必须有道路线数据,其原理为将各轨迹点匹配至与道路垂直距离最近的节点上,并且将前后两点沿道路顺势连接。

     

           优点:轨迹全部在道路上,展示美观。

           缺点:依赖于道路数据的完整性,并且展示有失真实。

    3.2 基于时空聚类的去噪算法

           此算法的思路为在尽量不改变轨迹点的特征情况下,通过轨迹的时空聚类,将某些特征相似的点聚在一处,从而减小由于轨迹点打结导致的轨迹杂乱。但是本算法并不仅仅是常规的空间聚类算法,由于轨迹具有天然的时间属性,不考虑轨迹的时间属性进行聚类则会丢失轨迹的许多特征信息。例如,双行道道路,来回分别在不同的行道路上,如果不考虑时间维度,则会出现将双行道上的轨迹点聚类到一个行道上。

           优点:解决了轨迹点打结展示问题。

           缺点:对于轨迹点之间的连接并没有平滑处理。

    3.3 基于卡尔曼滤波的轨迹优化方案

           卡尔曼滤波(Kalman filtering)一种利用线性系统状态方程,通过系统输入输出观测数据,对系统状态进行最优估计的算法。其在轨迹处理中,可以达到通过预测轨迹点与实际轨迹点之间的偏差来进行轨迹位置的调整,实现轨迹的平滑。

     

           优点:轨迹线变的曲线平滑,更加美观。

           缺点:平滑度很难控制,导致轨迹有失真实,轨迹特征变模糊,并且轨迹噪声点对整体平滑效果影响很大。

    3.4 最终拟合方案

             以上方案各有优劣,但是如果能够进行有效的综合,并且再加上其他的数据处理流程,轨迹点的质量将会提升更多。具体描述如下:

    1. 通过坐标的准确度参数,去除一部分误差特别大的轨迹点。
    2. 通过计算轨迹点速率异常变化,去除一部分“飞点”。
    3. 基于时空聚类算法,进行轨迹点聚合。
    4. 通过卡尔曼滤波进行轨迹平滑美化,且控制平滑度过大。
    5. 针对车辆轨迹,在存在完好的道路线数据时,采用路网匹配算法。

     

    4.动效,让轨迹展示具有“观影”感受

    4.1 平滑动起来的人(车)

           静止的人(车)图标无法表示真实的变化,随着位置坐标的获取而跳跃变化的人(车)图标又颇显突兀,如果能实时的、平滑的显示人(车)位置变化将会使用户具有更强的“参与”感。我们以人的展示为例,为实现该目标,我们设计了16个方向的人员图标,根据坐标点之间的角度计算来选择不同的人员图标,并且通过计算两点距离和播放速度来计算人员前进时的图标变化,最终实现诸如前进时人手的左右摇摆效果等。

                       

    4.2 流动的轨迹箭头

           轨迹箭头的流动除了可以增加整体轨迹展示的动效,更能够表示出轨迹的方向特征。但是轨迹箭头过于密集,也会影响轨迹的美观。综合设计,当地图缩放到一定级别后,此时出现轨迹箭头流动特效。

     

    4.3 类“滴滴”的多轨迹展示效果

           “滴滴”这类互联网轨迹展示方案已经越来越被大众所接受,尤其是多车辆(人员)的实时轨迹展示上。通过在轨迹大数据存储、轨迹信息流、前端展示算法的不断研究下,目前展示上基本可与之靠齐,并且也先后得到了实际项目的验证,其中某扬尘项目为监控整个城市1.6万辆车24小时的实时轨迹。

     

    4.4二三维一体的轨迹展示特效

             在三维场景中实现对轨迹的展示,将更加具有视觉的直观性、冲击力。并且借助游戏场景展示的思路,将会让用户有更好的互动感。

     

     

                         -----欢迎转载,但保留版权,请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/

                                                                               如果您觉得本文确实帮助了您,可以微信扫一扫,进行小额的打赏和鼓励,谢谢 ^_^

                               

     

    转载于:https://www.cnblogs.com/naaoveGIS/p/11154198.html

    展开全文
  • Windows7快捷键大全

    千次阅读 2013-07-28 23:10:58
     按住右 Shift 八秒钟:启用和关闭筛选键    按左 Alt + 左 Shift + PrtScn (或 PrtScn):启用或关闭高对比度    按左 Alt + 左 Shift + Num Lock :启用或关闭鼠标键    按 Shift 五次:启用或关闭粘
  • 点上方蓝字人工智能算法与Python大数据获取更多干货在右上方···设为星标★,第一时间获取资源仅做学术分享,如有侵权,联系删除转载于 :本图文来源:艾瑞深校友会网、网络封面图片:邑...
  • 点击上方“3D视觉工坊”,选择“星标”干货第一时间送达整理:公众号@深度学习与计算机视觉本文仅做学术分享,如有侵权,请联系删除。这是一篇在知乎获得220多万阅读量的话题,先看看具体问题,...
  • 该教程展示如何使用Jenkins的Blue Ocean特性生成一个流水线,该流水线将协调构建一个简单的应用程序。 在学习本教程前, 建议您先从Tutorials overview页面至少浏览一组入门教程来熟悉CI/CD 概念 (与你 最熟悉的...
  • android安卓源码海量项目合集打包-1

    万次阅读 多人点赞 2019-06-11 16:16:24
    │ │ 自定义Edittext 带删除按钮的 Edittext 附带旋转透明动画效果,当输入内容时 右侧的删除按钮会出现,当输入框为空时 按钮会隐藏.rar │ │ 自定义EditText带加减的.rar │ │ 自定义固定头文字的多行文本编辑...
  • 本文转自计算机视觉联盟,文章仅用于学术交流。Harris角点检测Def.[角点(corner point)]在邻域内的各个方向上灰度变化值足够高的点,是图像边缘曲线上曲率极大值的点。[基...
  • 我的技能时间交易平台小鱼儿网成立最晚,但却走了最具互联网思维的盈利之路,增值服务盈利,平台在整个过程交易中不收取费用,提供大数据分析,筛选服务者等增值服务,主动权完全交给用户,互联网时代,流量为王,...
  • Git之(四)分支管理

    2017-11-13 11:04:58
    Git显示,共有两个分支,当前工作分支为master,分支列表中的号“*”相当于HEAD指针,标注了当前工作分支。 切换分支 命令“ git checkout ”可以将当前工作分支切换到名为branchName的分支。比如,...
  • 分析这块,分组、筛选、下钻、新建数据字段需要写公式…这些还是有点难度,一般软件深入的功能都比较难,需要研究一番。     4、 产品功能 此次选型重点。 Tableau是直接连接数据库(相当于...
  • 专升本计算机文化基础练习题 1、 世界上第一台电子计算机诞生于 ______A_ 。 A . 20 世纪 40 年代 B . 19 世纪 C . 20 世纪 80 年代 D ....2、 世界上第一台电子计算机是 1946 年在美国研制成功的,该机的英文...
  • 那么从数据源头出发我们需要根据算法的输入形式和感知的需求提出技术指标,筛选Sensor和镜头模组甚至进行定制化业务。 2 泊车感知的输入形式? 在目前L2~L4的方案中,摄像头作为核心传感器通常部署在车辆周围一周,...
  • 重采样方法 对样本数量大的样本进行欠采样,即删除部分样本;对样本数量小的样本进行过采样,即添加样本的副本。 在一定程度缓解了不同类样本数量差距悬殊的问题,但降低了数据集质量,增加了训练的误差,训练的...
  • 筛选出显著特征、摒弃非显著特征,需要机器学习工程师反复理解业务。这对很多结果有决定性的影响。特征选择好了,非常简单的算法也能得出良好、稳定的结果。这需要运用特征有效性分析的相关技术,如相关系数、卡方...
  • 要更深入地分析这些性能指标,可以运行MONLBL(逐行监视实用程序)并使用号通配符%sqlcq*指定例程名称。 请参考使用^%SYS.MONLBL检查例程性能。 缓存查询:自动生成的缓存查询类名。 例如,%sqlcq.USER.cls2表示用户...
  • 以前面的搜索query“下载深海大作战”为例,经分词工具可能切分成“下载 深海 大 作战”,但其实“大”和“作战”的紧密度很高,从文本相关性角度来看,召回“喵大作战”app要一定程度比“大人物作战”会更相关。...
  • 目录 第一部分:Windows10系统快捷键 复制、粘贴和其他常规快捷键 ...今天与大家分享一下最全的Windows10键盘快捷键汇总,包括:...Num Lock + 号 (*) 显示选定文件夹下的所有子文件夹 Num Lock + ...
  • 内容简介 算法已在多大程度上影响我们的生活?购物网站用算法来为你推荐商品,点评网站用算法来帮你选择餐馆,GPS 系统用算法来帮你选择好的路线,公司用算法来选择求职者……当机器最终学会如何学习时,将会发生...
  • 即使我们删除钻石的顶部 A 类并允许多重继承,我们也将看到这个问题含糊性的一面。如果你把这个理由告诉面试官,他会问为什么 C++ 可以支持多重继承而 Java不行。嗯,在这种情况下,我会试着向他解释我下面给出的第...
  • 即使我们删除钻石的顶部 A 类并允许多重继承,我们也将看到这个问题含糊性的一面。如果你把这个理由告诉面试官,他会问为什么 C++ 可以支持多重继承而 Java不行。嗯,在这种情况下,我会试着向他解释我下面给出的第...
  • NLP在网络文学领域的应用

    千次阅读 2018-12-28 08:23:53
    删除短句,只保留长句。原因是加入短句会使得最后的命中结果太多。 去除长句中的命名实体: 保留常用词,减少实体词的干扰。 提取指纹: 通过MD5等,对每个长句提取唯一的指纹,得到该章节的所有指纹集合。 建立索引...
  • 计算机编程 常用术语英语词汇大全 记忆

    万次阅读 多人点赞 2017-04-14 15:13:21
    级联删除 (fordatabase) cascading update 级联更新 (fordatabase) casting 转型、造型转换 catalog 目录 chain 链 (function calls) character 字符 character ...
  • IT 常用词汇(一)

    千次阅读 2017-08-21 19:39:14
    号标出 52 , kick [kɪk] n. 踢;反冲,后座力 vt. 踢;反冲,朝后座 vi. 踢;反冲 n. (Kick)人名;(德)基克 53 , descended [dɪ 'send ɪd] adj. 出身于...的;从一个祖先传下来的 54 , hood [hʊd] n. ...
  • 计算机常用算法对照表整理

    千次阅读 2017-07-26 10:58:01
    cascading delete 级联删除 (for database) cascading update 级联更新 (for database) casting 转型、造型转换 catalog 目录 chain 链(function calls) character 字符 character format 字符格式 ...
  • 4ifelse配合地址筛选  11:40  171. 5函数收费  7:51  172. 6函数调用过程  3:38  173. 7函数基本小结  6:02  174. 8return小结  3:57  175. 9位置参数与默认参数  5:17  176. 10混合参数填充  3:49  ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,880
精华内容 752
关键字:

如何筛选删除线的行