爬虫学习_爬虫学习心得 - CSDN
精华内容
参与话题
  • 要知道学好爬虫对工作大有裨益,可为今后入门大数据分析、挖掘、机器学习等领域提供重要的数据源,从而奠定一定的技术根基。 那么究竟爬虫是什么?首先来看看官方定义: 网络爬虫,是一种按照一定的规则,自动地...

    据不完全统计,世界上80%的爬虫都是基于Python开发的。Python简单易学,对编程初学者十分友好,而且具有丰富而强大的库,开发效率奇高,因此很多编程爱好者都对Python爬虫十分感兴趣。要知道学好爬虫对工作大有裨益,可为今后入门大数据分析、挖掘、机器学习等领域提供重要的数据源,从而奠定一定的技术根基。

    那么究竟爬虫是什么?首先来看看官方定义:

    网络爬虫,是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫

    换做通俗易懂的话术解释就是:通过程序在web页面上获取和筛选我们自己想要的数据,为我们所有,也就是自动抓取数据方式或者功能实现。

    其实爬虫涉及的技术非常之广,包括但不仅限于熟练掌握Python一门编程语言,如: HTML知识、HTTP/HTTPS 协议的基本知识、正则表达式、数据库知识,常用抓包工具的使用、爬虫框架的使用、涉及到大规模爬虫,还需要了解分布式的概念、消息队列、常用的数据结构和算法、缓存,甚至还包括机器学习的应用,大规模的系统背后都是靠很多技术来支撑的。爬虫其实只是获取数据的手段,深入分析、挖掘这些数据才能收获更多的价值。

    用Python写爬虫,首先需要会Python,把基础语法搞懂,知道怎么使用函数、类和常用的数据结构如list、dict中的常用方法就算基本入门。作为入门爬虫来说,需要了解 HTTP协议的基本原理,虽然 HTTP 规范用一本书都写不完,但深入的内容可以放以后慢慢去看,理论与实践相结合后期学习才会越来越轻松。关于Python爬虫需要学习哪些知识,为了方便大家学习,小编特意整理了一张Python爬虫学习线路图,希望对大家的学习能有一定的借鉴意义。(含配套学习视频教程~)

     

    视频教程传送门:

    Python爬虫从入门到高级实战视频教程:https://pan.baidu.com/s/1bRUqsxozqe-yk1ZguyoCwg

     

     

    展开全文
  • 从零开始的爬虫学习(一)

    千次阅读 2019-05-10 16:33:13
    本文介绍了爬虫的环境搭建,一个简单的图片爬取例子,最后介绍两个常见的小错误, AttributeError: ‘NoneType’ object has no attribute 'find “requests.exceptions.InvalidSchema: No connection adapters ...

    本文介绍了爬虫的环境搭建,一个简单的图片爬取例子,最后介绍两个常见的小错误,
    AttributeError: ‘NoneType’ object has no attribute 'find
    “requests.exceptions.InvalidSchema: No connection adapters were found for…”

    什么是爬虫?
    这是一篇解释的比较容易理解的博文。

    从零学习爬虫,逃不开安装环境

    完成安装环境,下一步就是安装常用的爬虫包了,这里提到主要是这两个包,request 和 BeautifulSoup (美味的汤)
    这两个包是比较常见的,也比较好用。

    import requests
    from bs4 import BeautifulSoup
    

    这两个包都可以在Pycharm中直接下载。

    File–>Setings–>Project Interpreter
    在这里插入图片描述
    点击加号,搜索添加包在这里插入图片描述
    这也是添加引用包的方法,当然手动添加也可以,这里就不说了。

    安装环境打好后就可以开始编程了。
    博主之前没有学过py,所以介绍的部分可能比较基础。
    废话少说,直接上代码。

    import requests
    from bs4 import BeautifulSoup
    import os
    import lxml
    import random
     
    url = "https://www.baidu.com/"
    
    r = requests.get(url)
    r.raise_for_status()
    
    bs = BeautifulSoup(r.text, 'lxml')
    image = bs.find("div", {"id": "lg"}).find("img")["src"]
    
    ir = requests.get("http:"+ image)
    if ir.status_code == 200:
        open('logo.jpg', 'wb').write(ir.content)
    
    

    本例实现的是从百度官网下载百度logo。
    主要用到了 request.get 函数和 BeautifulSoup.find 函数
    具体实现参照了这篇回答

    简单总结一下我的理解,URL是一种网络寻址的路径,用来定位资源,和网站网址类似。
    r = requests.get(url)
    get函数的具体结构可以参照这篇博文
    get函数模仿主机去点击网址的方式,给网站发送请求,并接收回应,get的返回值是网站反馈的Request对象。
    r.raise_for_status()是建议反馈的Request对象是否正常的工具,具体内容可以看这篇博文

    bs = BeautifulSoup(r.text, ‘lxml’)
    image = bs.find(“div”, {“id”: “lg”}).find(“img”)[“src”]
    这里讲BeautifulSoup实例化,因为是由get的返回值实例化的,调用find函数去返回的网页中找百度logo
    如果报错
    AttributeError: ‘NoneType’ object has no attribute 'find
    请检查find是否能找到具体目标src。
    在这里插入图片描述
    可以这样理解,find先去找 div类中 id=lg 的位置,在去找其中img下的src,将src给到image。

    ir = requests.get(“http:”+ image)
    因为 image中存放的是 //www.baidu.com/img/bd logo1.png
    我们要去访问他就需要补全网站 http://www.baidu.com/img/bd logo1.png。
    ir = requests.get(“http:”+ image)
    否则访问就会报错
    “requests.exceptions.InvalidSchema: No connection adapters were found for…”

    open(‘logo.jpg’, ‘wb’).write(ir.content)
    最后打开logo.jpg存放图片。

    到这基础爬取已经完成,下次将对具体的漫画网站进行爬取。

    展开全文
  • 快速学习爬虫

    2019-03-08 16:13:25
    快速学习爬虫爬虫简介爬虫快速入门12306快速爬取信息 爬虫简介 网络爬虫(英语:web crawler),也叫网络蜘蛛(spider),是一种用来自动浏览万维网的网络机器人。其目的一般为编纂网络索引。 爬虫快速入门 学习...

    爬虫简介

    网络爬虫(英语:web crawler),也叫网络蜘蛛(spider),是一种用来自动浏览万维网的网络机器人。其目的一般为编纂网络索引。

    爬虫快速入门

    学习爬虫需要一定的python基础,有了python的基础,学习起来会容易很多。
    本次用爬去12306的余票信息为例,来讲解一下爬虫的使用技巧。

    12306快速爬取信息

    import requests
    url = 'https://kyfw.12306.cn/otn/leftTicket/queryX?leftTicketDTO.train_date=2019-03-09&leftTicketDTO.from_station=BJP&leftTicketDTO.to_station=SHH&purpose_codes=ADULT'
    response = requests.get(url)
    print(response.text)
    

    大家复制这段代码运行,查看一下控制台输出内容

    {"data":{"flag":"1","map":{"AOH":"上海虹桥","BJP":"北京","SHH":"上海","SNH":"上海南","VNP":"北京南"},"result":["oKFogADXCkMsUY2Aomov%2F%2FS8jKRc3JQvQI63UcZbPY1n%2FdFRGuR3snpDzNFTqFj6dZPhLV6ng57i%0ACzbQNt6GPRBkruxcK69l2m3C00xwO98Dli%2FgUdc5bM%2Bcp7MGUf%2BZCUtwBwM2RkPaxCUIGJ3srvzq%0Axm1XCaaxdoyDrJTj8ScIxOU339bFz2gI39NNAqtFTINW7NGx%2BSsMzctiAc3Ehhdlyf9FsC3J6idH%0A1MQcXBGzUeBlf9xZ9lyIY%2FZ9zWUfRLD9Yu9LwQp4P4UoCEq282Nk7gISDloqVNZqhSj05pXHmvjn%0A|预订|240000G1010I|G101|VNP|AOH|VNP|AOH|06:43|12:40|05:57|Y|ZUPplMKmanGoJw1CLqNTpzelj4cAKHQQbHbeUN9cvB6Vat2X|20190309|3|P2|01|11|1|0|||||||||||有|有|16||O0M090|OM9|1|0|null","bQZRcTpOtu1nrmNMQLGQk%2BwTqQ1jYrMrkB8tjtQvCBwhwvZ1XJe4Zh8Tj7Q1%2F5LzHuQaYl8o0Nqk%0AQ%2BQmQXFU5DFsYqQD6TE5QjtNCpOJBz2m1k4hXBhpWnNljujZ%2BuVulMTpj5%2BV0jWcMu0VEM7mmYMv%0ALXARNvzW8yZFaXWb%2B%2FLbvgBdEG3hNYQ3PVI9%2F0C4KrndDFv2sqUvXttXIJpF%2BVmRmviUryOwc4vQ%0AX%2FjxwlZr7y5awLDGeBMkWHzaO7Jss0tyAhvA0dnvb7PIHi63WanF2Lc2dSknXXCXqQ%3D%3D|预订|24000000G505|G5|VNP|SHH|VNP|SHH|07:00|11:40|04:40|Y|DvKkl%2FlHBu1tZHWIv8DaXrieS0JettVLlAT49aG%2BJs0UzPL1|20190309|3|P2|01|05|1|0|||||||||||有|有|5||O0M090|OM9|0|0|null","PCRw5eeuaQ7SrbFl43UMhs%2B85DJLP%2Bv5xyoE09y%2FmXuA392%2BVk93DGq5wG4ojKinZcDQfiOSmxp%2F%0AWq2Lanaaj8NrScjI7GxUeZ68YulCxOYS%2FEkDUcEDIUGojagfO0aaJrzYDdeW7E2Xkp21gRAsi9vP%0A1d6IEWKWOijkgxEpVoQS1jeuuBwe%2FJozlXFHJio8ual9ZTyTN6%2BqHntyY4%2B7DEuJoX0clN0RBORi%0AmR8Vr%2B6AYA7XDfDskcgD69uy8YfuXDgjZrAlFLDdQfUvOe4GGhEPtmMwZRTn5QnnQBSRtHV3Zbd6%0A|预订|240000G1050N|G105|VNP|AOH|VNP|AOH|07:20|13:08|05:48|Y|FCiQgZA%2FiYe33ZTWOldTao5p%2FV2xqnj6KsKzadbXKoJyFT%2BI|20190309|3|P4|01|10|1|0|||||||||||有|无|无||O0M090|OM9|0|0|null","v%2Bt2XrTN223s16ru7HpIivk9skZ99oXk1N%2F2RL7DqPi29XAY1hRZuNH5YM%2Fr7fP%2BrkxZquG0CCZr%0AEbKY0MrUc8toze6unniftZEk7HTI73kHnxSfWgiGY8QJutHo5bfZ58p1E12kxTTbXrfsE%2BF%2Fxpke%0AIdvo%2BqlwcmwxKx2bBa4BVERswtu7xqgh7gDztFRgzkyGJcMyhgqxmNkdGzDdx%2FuAlBokk7QyCXgl%0Aft27GMqsqMQhQWbwtY1wg52XXOm3UJrls%2BHi9s6tyW4JuffuUISsDtdkhshl7N6j5kcVWPMQAmqX%0A|预订|240000G1430Q|G143|VNP|AOH|VNP|AOH|07:50|13:12|05:22|Y|S3eBP6f2wuiCi2zkbwL9Oh76E9lz40TwemsQHQ7k8I6shyAz|20190309|3|P3|01|06|1|0|||||||||||有|9|无||O0M090|OM9|0|0|null","YcZO2ANHDs6izYRnFswy0e4gccSPb0iG7qlD3i1bU8Q9LYWIgiBI4ObLKIAxlDRxbudSofI9yFrS%0AQMUg%2BLQKUfYJZw6SiAN6mcIKt2z6ZOhMPgByezWYbKJ5RpzyCX2ny922Xgk0tqiDmDqaq8OWjBcJ%0AE1%2FgAwbCJd%2BoOCdUalJUxe97idxF1Su8Pq5e3hNmw1hJmM6XCpH0F%2FCUdMDEv5CsFyfO0IXxDruN%0Awt3PatKzDCXqYqiM1SRJr%2FZ9EwcRQNo0VHliSvrFJqo9F7L6h%2FC17cezvIu2Q5aSdW7ggXWGEQ%2Fw%0A|预订|240000G1070J|G107|VNP|AOH|VNP|AOH|08:05|13:46|05:41|Y|5zjWZODph20H%2BHon9Wv3SjRTWUwjx3%2FfYDx0l%2FL2MFn4OP9P|20190309|3|P3|01|09|1|0|||||||||||有|有|6||O0M090|OM9|1|0|null","zPT8fM9Priews%2FMwaF5bq0dtqx7LNpu1402HoVgTSdQxD64Zx%2BWVTXaCdHDBmFtl29GtdS4Cpqey%0AR3hSUq2XGH6T4IbONdDtETnPwAdpwgNfSQVS0F%2BJR%2BRLz0xP78zRaKDUT7E3%2FixXSNUjbYScwJZ5%0AL%2BimH7bhJod7pA1Y7XgVYmW4t0TkPtm03yPij%2BC6wrzPxOE6QVWiAjYeBP5oWIVeNUOpHfz8xOCM%0AbsDThkKgdKY%2F3tWhtKR%2BsSj67BDK9NW%2BVQUvNGycMMX0EmN898uFLsKbWYFMrYiElhfGe7qp7sFE%0A|预订|240000G11112|G111|VNP|AOH|VNP|AOH|08:35|14:22|05:47|Y|%2B%2BFA3i93rzaHF28VfSxeyfLixbEZjcuT2ciPvlcLEQV%2B0iLM|20190309|3|P2|01|10|1|0|||||||||||有|有|7||O0M090|OM9|0|0|null","idbp7jtCPkrF5BUmNrb%2Br13kRT82ANnG8PqYo59Qy9a39bIM%2Bx3DvStrc4hFlf6haEj%2Bz2yElJ3P%0A%2B1Blahyxp8RfIvroC1lDNJkfAXJYESFHoq2oaA7c8OZW3xnauWoRLDhi1%2F%2Bx4JzsfMqV5GNoVYHV%0AFP6oQ4eUFF5htCUlclyy19a7UJBZYTljvF855IryT6HKPEV5yhO1fQqgPvMwYw%2FuW%2BiAp7tev6mT%0AGZXkKLWX6bjJVjQOlbNGNcZNQcxINZUgBEyAWIfttk%2BKQ%2FguynyObjLC%2FTt75ep%2BlZFb6wXlgjcy%0A|预订|240000G1130Q|G113|VNP|AOH|VNP|AOH|08:50|14:33|05:43|Y|QGMUsQQvHlFdqSG7zTZkE5vfs743%2BvsUr3CQH6cItrfAbeY7|20190309|3|P3|01|08|1|0|||||||||||有|有|10||O090M0|O9M|0|0|null","lUa%2BtZsqCLZPo9i%2Fm1qWjPJaOXQcvmcjPh%2FMB4zewQbFHNP8R5exJQoAIu5Uh9pYxXzKLS8Zu6bM%0A%2FlWHmN0SBZrRbxX%2F3vQyxODx7vvTeya3htOBn2Bz65pxBx4HCYpfdrwg3B6RS35kaqP0tWG3bP06%0AkSpSYss9tGlstBo%2B3POrqSOYWzJiH0Rdi3cnXjFX7AA7esKjBgzKuDeTIdz0GmE0a3ODOrr2GWYZ%0AFqK9HxEErLT4dyPdNcoFNNZecbh2Rt%2FzSDXEckVQl1SeJ62TgOvbeLboHM1u15jAHEItjrSC2%2FXO%0A|预订|24000000G10G|G1|VNP|AOH|VNP|AOH|09:00|13:28|04:28|Y|%2BniJpi7WQT6HcFrax0B8zNTXCow9OvKcgSbZho2jnjv2QZUd|20190309|3|P2|01|04|1|0|||||||||||有|有|无||O0M090|OM9|0|0|null","KbwjzWGwqeswhBU9dwjRZRrm%2BcI4qYT6agq%2BJB3QfGm7KnrbBefZcvQuO%2B0lKdSMAFAL1BW4Btoo%0ANYUP4nZ9OQM1y9cuxk4vQ22P%2FyWls0hiANbwfcadCRfjT%2Bwp1KrTLKpz4%2FttVOHSqg5tAlgteAlJ%0AA18%2BCAy4qeHeYYYf8UA0bJYnp3DGffuB1CSFBgBdrpqpzHptevLqXahGlwqvbOX6RD6syUMHaxrX%0Azf1peXyiaR1SydB4ATB0nE6bcjgh%2B8W3dfz2qKzHSOKgLcIoHF3SNgmgsCZdMWaGCYu0%2FNPjVFJq%0A|预订|2400000G410N|G41|VNP|HGH|VNP|AOH|09:15|14:49|05:34|Y|nHOhBC%2Bim3EFUDrAQ79RKRjlYdcnSudVPRgeFvuFH8oXfhgg|20190309|3|P2|01|08|1|0|||||||||||有|1|无||O0M090|OM9|0|0|null","u%2B4SQrJ7CIP1F%2BKAGS9PyFEFZWT4rJb%2BoeVpUQapU9uhD4cN2CqTjbVqA6sWbXLxCYQR7UvhdVzu%0A%2FWbXzUfhdqEwgM%2BHjJY9SdBlHub6aHDmCzsRWgoguGa1oDfdA30qbsjYnxOlsxySpL38mOEQmn3G%0AOFlopg5DBD5tnKItqLl1FaNzLEAkexSd9jQdE9QuHs%2FLNBzbNi2C7moUfvFVQFqnFuSJYhk34cpF%0ArQ3vV6Vv7lO%2BVwm6Zd7tvtQVEqld9j0WFGCNFNTSxGzE6bEDP0CDwkuKyWwckvWBoo7psvUgtT9m%0A|预订|240000G11513|G115|VNP|AOH|VNP|AOH|09:20|14:59|05:39|Y|GcHwiegS4njC82v8hhSov7Ula1dxnvPKHcTIgDDf%2F5rBIs0I|20190309|3|P4|01|08|1|0|||||||||||有|无|无||O0M090|OM9|0|0|null","d2axnUvOLk0bKWc6%2FGPVMA7YoWrRzFZkj%2Bq7d%2FaIFNjtODmZxvPhC5qSQoi1tIqP%2FXpX5kSxcVvB%0ACRktdtm9anLHEGtpD1HSxknWrxD%2FaFH36iynTM5aVxpGo%2FOzrcxJAHkDbFBeEuWwjFBs8lP0iOu4%0AEJOVosWj6HREe8%2BMuzrbev15dDU75JyMJWkgowFelbiL12DoBbSTCpZWnx8JPU89Zd2WHbE3ll%2BI%0AZo2Tqn%2BIb5%2BvRZcWyVKAIrjcwrDrcuLEHkgpgJunihmKGhiePR7AJbI52ePrn5smpDcboSUWvQPz%0A|预订|240000G1170W|G117|VNP|AOH|VNP|AOH|09:25|15:37|06:12|Y|Y3iC8KFZ5Ndc29jS0jwbDF956CuxSUBxXHTSJjAqkx%2F%2BviLT|20190309|3|P3|01|11|1|0|||||||||||有|有|9||O0M090|OM9|0|0|null","On7qzIfoTCqnfck36ESde%2BkA6vca5uf21lWzgGZ%2F4fA%2Bl9msu9UdpXUHL30mrBZU6eWFlI2dqXid%0A%2BhYyATjZld1Kto4E%2FCavNo9Umq0LTs3UdXC97XGXdWcACAqd2qHep074BjxRaDVprBPOluZRg5Zz%0AjevS40Suzzpb%2FQ386xCopO%2Fs%2BCscG7I%2BWApKn%2B9in1EWGmiKLepndFN18F3Wb0fzYkumtzcW%2BC32%0AIT%2FOnnTP5ofUb2KxKtwyVRb0KeEZH6pNOVMq0rUlKG8b3niMqb6Dm1aDxSpn2ZVafwarOjXYo0yp%0A|预订|24000000G706|G7|VNP|AOH|VNP|AOH|10:00|14:28|04:28|Y|2IyDVlYwC%2BcTdJIdIdVZWMu1yLv0%2BtBclBYkAtoViCPy1w%2Fm|20190309|3|P3|01|04|1|0|||||||||||有|无|无||O0M090|OM9|0|0|null","W%2B6ep6Iz%2FVwlOrQfww2DfLZf24xnZsHF84zrZf%2FUJWgWwX0IBFJkEnzJWXbLrMBU0nLIcR%2BJJSwj%0ABfbWTchFkBHTqFonHpyJdcTzpySUcJfWokMiWAqu%2BR3f08XDv46Eq5u6z6Pk8cllD%2BbfTgo93OsR%0AGy2G0IyYRTN1Us5SfUz%2Bzb3JW6bJuTnkRoZQ50fkgJgG8aVb28k%2BwXafdHIsuA0l70DGOtP35SCq%0AQSebQEhTbvmOo1QCNWr%2BpyY%2FtapVAb8Pt%2F4Yb2kTiLoEYmclbMUHadV4XxZTGWFpN%2BJPZYUmAs0w%0A|预订|240000G1190F|G119|VNP|AOH|VNP|AOH|10:05|15:47|05:42|Y|3HMI7N%2B4oYsLt7fzDM8wXcbCub2MFea8uzCnPXwlPHrY4QNq|20190309|3|P4|01|10|1|0|||||||||||有|有|10||O0M090|OM9|0|0|null","JQmHDx6tk5%2B8YzANjw9UOlpIxB4TlrUDwbvyS9pSpeCyvFX3MrkH%2FdGuAur8JUx2VwSGKaUEj25W%0AJQoltUTIRVfO7lGB9EULv2MRa75Yv9VKs6Ep%2FWzKea7AxoZnmsUMRz9QGDtSTBLnz3naC0yOZ8p0%0ASNOSBfRwEtpskFdykx9aGhyiMc5TZp8lVbpxgASlZMJBhYUS6G4guf%2Fh2mTuWeWHYxEwKspnjBF%2B%0A3Ki3G2zwn0wsSPXDsewp%2Bgy722Cmzmsa9dxVB2zMmm1Z6u4A5pQi3uO3MmZ1qfLizRMmXdGjLSTS%0A|预订|240000G1210L|G121|VNP|AOH|VNP|AOH|10:20|16:25|06:05|Y|K7e0jV%2Bi59psVEYpDNrHpotS1uuHRlb31oyp3rqcsd8GyPDn|20190309|3|P2|01|10|1|0|||||||||||有|有|11||O0M090|OM9|0|0|null","0K3rldFm719YiDNf8%2FKOTBZYsWMwjBRK%2F8b0fW%2Fzh4iJBTXO5ts5yJ6k%2BPCWqmC2loYbBIfrHxh2%0AlfUu6IH1eHr15PjIv4%2Fg5GeIBPuem9%2BkwqLDmb13Wus5%2B4FWo1CdVWMYCsxbgqfboOs1knoktDeS%0AFdDXr8uboXAOI3C70QEshuYFyloLE0%2BzrmJ5TmAttnYWzY9xRheS492CVr96GgxgWmRswXlNXtaO%0AOhqkcXRiOyuwT039r9PxSNWbbKmJxuxQa1hBhq7LAMUBT2FrAVIUMz8hePOwCZhJIOAZhG%2Fece5R%0A|预订|240000G1251B|G125|VNP|AOH|VNP|AOH|11:10|16:59|05:49|Y|2TdkmwUxfSxQKLpgw6NCtJqnGE0JFpcGud5NHF2tjRlY8jKe|20190309|3|P4|01|11|1|0|||||||||||有|有|4||O0M090|OM9|0|0|null","4nwu2fLmXmRk2VLvyBZH38BP8QqDdJm0zaPGsvylwoy9ROSkL136MlRtos5Fd9CTRGHiiG04udVT%0AXRyLm5TfR39RrJH67oW0nn7A%2FdNSwGuczKP6Jc%2FqyGBtHC7i2%2Fb7X3m%2BOLpJ8xpGHU6Tl6t9GCPh%0AaKOWNt%2F0ki5ckTY8qkUpGEEppzvZimRXx2KI1rQXX96JCq9dWPwPBR362Qzh69%2FAAP4c5Jge9l37%0A6aw1%2FSpuCIOItAA0eL5shPTtMvJLGfN0Bq0vhgvfRMMdASU42mcSIU%2FFFhLUSimSTeC1r8wdJ6O4%0A|预订|240000G4110C|G411|VNP|AOH|VNP|AOH|11:20|17:25|06:05|Y|e%2FW8AXIimdElmGx%2F58r4mMloRXE8MC69m%2BxIKcfXtaZbnUpa|20190309|3|P2|01|12|1|0|||||||||||有|有|12||O0M090|OM9|0|0|null","39pMgam%2FpYWWPk%2BIRLYTOWanG6yNr5IjiDGcQJyo%2BXUbiQ1YqH%2B0LTXmx0%2FyDkoB4e8Of4byOH00%0AkYLGFOsVBTt2KFm3Q7HgMa7JxhSh4ICsJDskBB9d3QCi2T61Om5NGIdXyTO9e2LdBFiZEhAz%2FO3p%0AKDHnuEymgYYJOa2K9kEgHcnGAhBkOw%2FDO0Wv%2Fw2uElOyvzNnFdJ7KlIWH6VUABGhMoBllZEDn%2FHO%0AKQbYtAYqMBzhUmzIhv648yyODumlH2LUgWpk8%2FkHUfFo1hKCWLU4v5vXGDiJbvkfs%2FrBHu37MRAA%0A30kgTw%3D%3D|预订|24000014611W|1461|BJP|SHH|BJP|SHH|11:55|07:00|19:05|Y|Paj0hjBgrfTfKYj9YggJ%2FbsOHzD8vlJ%2FCljV4y2sHhEbzDa1L24CYUN%2B3vE%3D|20190309|3|P2|01|27|0|0||||13|||有||无|有|||||10401030|1413|0|0|null","xLfpolrq0R5jbZ3Ge1ioYHJ631sI9cT%2FPKRx5SJYiAQjICxUP6UbywHqYGmUUn8PCGZwxCYU61YE%0AGAZi%2F0TUiNKXHpXVoFex3ASydwGC%2FurXeIuA48qgOrRIsklanzZvY1V8woxLYFZ6JJXgJpnXUc7%2F%0As4jTRV9HFa8%2BaF9tuIw1kXmy%2FcOGQjzg9rrAkWgKZyAmAkapumcauw%2BHYRVCxV7Mt%2F9nT55ZDeSl%0APibLr5uh7Si7GH4W9%2F0qH9BAgn8R2Vf6g8h0NAos4ZMHEpPvZO7oYavLQgpfudlOW%2FZx2mgwIBE1%0A|预订|24000000G903|G9|VNP|AOH|VNP|AOH|12:00|16:36|04:36|Y|Jp9e0Sv2UJfyL0GAIyCDOH7Uz0XoXxmuAlPph7hTpEysbhcs|20190309|3|P4|01|05|1|0|||||||||||有|有|无||O0M090|OM9|0|0|null","GfDtg6yQnv8kq6CYOBiS7dIzY%2BpKuz10EtAftEaevBBk3Denhks8Ygn%2BkOW7YzD%2FInBNyDgjw528%0AKSX8GawT8Jc6FwXv9HOumCFuv7PN02eAAB1ovz5dTrztaGXCVRGR7qr9XpxwdYB%2F4uFy9gOj5BWI%0Ai3sFMZx7O2z7haHjbkOM%2FFHFrBBkTSj1V%2Bo0hLMSZMcCxWv4uQ8xKgQ42IcHsDpRCFow%2B8pEK8K1%0AxYqTZCElzpDzFrwBHcPej2FoSDWFV1IFMStKWQ1XHeoNvPhxSnNKxRT0WxG2Hd3bm742iPA%3D|预订|240000G12900|G129|VNP|SHH|VNP|SHH|12:10|18:07|05:57|Y|EboUERS5MQdc%2B29NpQBQBy4BaH%2BdrffNCvGYTyoyyxED8hRx|20190309|3|P4|01|12|1|0|||||||||||有|10|2||O0M090|OM9|0|0|null","frtJIzQTLQqGorqZ0q1fR3vDIBqLt5yh5rTVA9PWaW06LKmGD0bX%2BcYumTOyYwGu1NaPOjMI9Bb2%0AWO9fLfgQDG0EL5IWaqWYyZ49%2Biy4RXBPz1t0I%2BS3M398yVzYQYxFj0yVoFw10jehiXXUFLRQoiLE%0ANFbi%2FvHaQR4vp7BH1Mbc3XcmsuffRLviF%2FfXY9d2361AfPWpPyi4VSYwcIIXKmMfs2SbmZAKB1RC%0AIMKbHT8n5JPthkLYtU4I6cz3I9L61uxxE2YWOUTRpUJekhWG52%2FixkuedWIwepkfhDAJIhEHNG5q%0A|预订|240000G1310Q|G131|VNP|AOH|VNP|AOH|12:20|18:16|05:56|Y|zcS57UgjdQTMxeOW%2FEbgCDqZVnjqeMDl3uYiZP2CzVTDbTKc|20190309|3|P2|01|11|1|0|||||||||||有|有|11||O0M090|OM9|0|0|null","m5hXkQBmonvFUyntDSOEdRE36mgIfCW1oGLGqVUROTCPIPAZdH5UUUnq0g%2FbN8xQbY2SFVP58Hct%0A8%2Fi1OWqh%2FXE19A79DljeKTaqhncEslvAfXXVXAj6q9KYTYICBP5ot4ywCpVB%2BRlQ605dV6oQJsyr%0AG6ZI4jVj0So9Thcftfh0as8%2Fe%2FhPnqGQ4Gat5m8kVi5tuKgGN0PFHASsQh0k1rF%2BT56sITBUFFVY%0A%2FFkMCEYBw5WXJe%2BIhPB1lo%2FbOHSBG9CBTu8LY4WGpuep7Xc9WDm%2BfxMoRWI19Kka0OT58W0PrR3z%0A|预订|240000G1330I|G133|VNP|AOH|VNP|AOH|12:50|18:40|05:50|Y|YCM4TKCTjUdlS%2BlCt8PLkleiE4aSU0p%2BcjVO37S9iQJh%2Bvs8|20190309|3|P3|01|10|1|0|||||||||||有|有|12||O0M090|OM9|0|0|null","8a5V073lR%2FjQkHTFoWjCNcRfFbg4z8x6QIoqMJnE%2F3tv2bUZ8qkZ2z1HVUnc0dyiCbfOy63%2FejAi%0AInaDnGN1OX5qpz8%2BCpmaVunxdz%2Fzr1rUG8EnXEWekDWAJEhDZ4BgO8d1NjIF%2F1ZV3ftAbura7FpX%0AHTGvm6lQX9wm6T40E8YYHJK5guwHICN2hYRj6Ike0FcAvvR%2FYq9UWpb4TnD%2B1EloapjFlq6OHG%2Fy%0AUSwjbOXk%2BVrPe7PkwjZIQro7K%2BsD5dp6UqTfOAKDzjxy0L%2FWy7ktLh8za0QV2AwKkqhOXp1wvJfS%0A|预订|240000G1350F|G135|VNP|AOH|VNP|AOH|13:05|18:59|05:54|Y|B5hadfvldtlfEnTh%2Bfiyq47QkPMbcOh%2BJN%2FJE8vp52ZYW643|20190309|3|P4|01|11|1|0|||||||||||有|有|15||O0M090|OM9|1|0|null","LRXzaJxxyFNWqu1%2BxEYVTJfiPGl5ELM4bYrY9D6wPEeD5ymBKWWlHO%2B0gRYGFe%2BJ%2F2QZD%2FTPw9Qs%0A9hv3s%2Bh06VH%2BDUqfMpMRpOG7VvrEQJGD0KetIB1%2FwZG7y%2FA1kMu4yp3ES5KZJ4bT4h%2F%2FA%2BqAPC%2Bd%0AiyXNzb%2FMWgeR0FmAoo7oHMrcVVwhWin9zPmr1eSiQR3guC68hU%2BDBfEvKonyXFED5C552tb9godp%0ApUzxm0vtQ4R7xdCN7lcBdFP%2BHMOqRgHujUS5pMI8XMt5caRywNj5Z7In8w04MjQVLTE%2Ff8oMMZ1w%0A|预订|240000G1370T|G137|VNP|AOH|VNP|AOH|13:35|19:17|05:42|Y|Ult1CB%2BALCJXZRAjGBl1AKAMQsIeGXZ4VyhhXH5S9bTd7aby|20190309|3|P3|01|09|1|0|||||||||||有|有|16||O0M090|OM9|0|0|null","uGmL5afoNBStokWtrciI6u7TiVrzv9OpT3JxcoTnsN91QisiEd%2BUDs29RelC4WH037QCzjCIHs6G%0AMzGSHOdKumtZxaYRjmNRRa1oH2dfHVeFoH8j2G9DXnPObdFVKNSlvgI88CT8%2Bi500PsbCGu%2Bb2Ad%0Ao0y%2FCMgqhu4YNSeIaJXWH2tWtzJurMlRsU27GRQe8kQwG3gYqckNoe03J%2FFdXllZmQzH9QGXf7cj%0Amd%2BM8eaxCsP8WW6QlahM7jovMQ7c8HzkSqf%2BWH0AWSBXGhFnHS7y0bNmhujPjJrjskNF3bsxiS%2Bd%0A|预订|240000G1390X|G139|VNP|AOH|VNP|AOH|13:45|20:05|06:20|Y|GaQl4c8ZosfeOFQbPiL9JPOLwmn0uRisARSO326KhVJoa8VU|20190309|3|P4|01|14|1|0|||||||||||有|有|10||O0M090|OM9|0|0|null","8jbAyydLLcVX0hp9GekG%2FsgkzE5jCdX27%2BKEa83unI04kxRPxrxKWVzWrIUUneE4Akwo%2FXjjpZ4K%0A1kYcX45gggMQNC2cZPUoUlJjFhiQl8yC1kh2iDTakVEOeGDBscOcvNjOFXsAQHKqFEfpwCPN9tkK%0Aip2W70ov0senKUIgVUk7puuAMZ0Y%2FnCyMIXDL8Xhtqk1ksC45P45Lu84m51UBaPvM2RkPy%2BLoXFq%0APCAgOjAqnYqe4Y1OTzuG5mTkZ%2BlMZ8hnhnPgcc4HadAUCwcqfA4OPdPbKO1AljIwUSiilVdY4acE%0A|预订|24000000G30A|G3|VNP|AOH|VNP|AOH|14:00|18:28|04:28|Y|jk3ejpNlirJVGTddVyh7HTwBjML4gam5mrsGzaUq4BoK3eFL|20190309|3|P3|01|04|1|0|||||||||||有|有|无||O0M090|OM9|0|0|null","surClUHW2R%2B4TqGClYTi6mayOtAjGLUBZdEQTKIise2vmsH4s2fRgbwFxloziXAt47w6ZKPatIVn%0AY%2FxXroimOSiOMCBteb%2B8g4YDna8YD5qEontdBuy4C4FKDaxPHAxP4hIC8QBec1eaD%2FORZThsSm%2BZ%0AfsXKfzN620oXajWODGgzIdbyy5Tc0ywy900Pl9InfEGte%2BhO07GLZl5Bo%2FHwGoevkryH6F0jzzGU%0ABXFkS2oAMDEm2ihDV8bzfBb4o9chK%2B5s7OYPvalmRYhoQFZbOsyNRKnDavW%2FJ9I1B2QetARxxiQd%0A|预订|2400000G430C|G43|VNP|HGH|VNP|AOH|14:05|19:43|05:38|Y|xTjQ4YD9Oq8YY2LlhfEgSywi0SEZOrVcVKpo7IKXIHQmwTqD|20190309|3|P3|01|09|1|0|||||||||||有|有|20||O0M090|OM9|1|0|null","eszA9mog2ylfKK1vtsl4g1Y8bptlwxjLruFUORVOAhcclGvIBFMkibCoKDLDtH2jzJICxbn711r%2F%0A%2FSGfNW0Zk%2BpcJ4fdOsmeHfDoqtwU%2BSaKP7zyMGJAEpJeaFOfrQiYCm41Bfk1la26dPszSzZdq464%0AdabEoHT3f7PczQOfXFL6N9MUH2UlMR7jKapfAIh3axicJd2rRGNZXY%2FrdKlsfAhlgekZlSFYSc9L%0AugLnc7yegMHLOZokbOAx3uRcrDSyg47KfpkEgbBS5mnwH8KIxc%2FfCLuIGVSVF03rAk1HCIIsYwgj%0A|预订|240000G1410J|G141|VNP|AOH|VNP|AOH|14:10|20:09|05:59|Y|%2FHPKBTwfoC8jiXv%2BB8MzljwthL5TNjFrde1jjMyltCe%2B5ugy|20190309|3|P2|01|12|1|0|||||||||||有|有|12||O0M090|OM9|1|0|null","LpM1b87EWt0bdGRhG1WarpJap%2B7zHIaziB%2FyMCg0WhURskNknpTg2AmeLJvo52CkYQyOWhoNjnLU%0ADN5WDChFhbarl%2F%2FvFnWX8ICGAAeZSFhciFnJApv%2Fn%2BQlj0%2B6rR4sMSrmyvrIfUM1reX5Yz6N%2F78r%0AfaJm%2F6j%2B7a12OxUvt%2B5j%2FbJN%2FuTcA7lT4wY6V1DyAe4PCAWvYnqOxoIpYucQKQ%2ByDWPayoVnCdlZ%0A%2F9nWkP80zM92LZhD3pkwTUWHzmu2e%2FJa5jSJudI7v6Z%2FwbJnwVZOfdKkucUPHAv4b1cAb%2BYlcxjz%0A|预订|240000G1450R|G145|VNP|AOH|VNP|AOH|14:35|20:33|05:58|Y|NAC1i9AMwVnhXlY7eH1nNV0ZcADBD6eA4mcHFiwK50vK7a2b|20190309|3|P4|01|10|1|0|||||||||||有|有|12||O0M090|OM9|1|0|null","GUTt0byxc%2FSCLo1URg3KBHsd%2FdY8bRthG3TCpKZ7pybA15K0W1TAvBrMRJTZq5%2BQcZ0Kp5FZjbO7%0A1iroL0OsaMqwRI1WyqwZ5ZNP5RE%2BRYE%2F2eq%2F0ozlZj%2Fhnl8VdNd9vVu4TddrTic9u1YNqNtAD1Sf%0AgU%2FLsL069C2tvBIapGK6w%2Fw8YvxRewD5HvZm4vWJx1tLxl9Ob7Xuoh8%2B2eNpzvVy2bgt4X99FHEI%0AYExuy%2BAB1UdwFnyBbrZQ72hJHDMWSLj1cjOL76TtJSvL0ClkUxjUgfLnIGBNHfgNSg75ejwSPFLa%0A|预订|2400000G110L|G11|VNP|AOH|VNP|AOH|15:00|19:28|04:28|Y|Ix2DUBtTAIq44%2Bi4YSSbR1YdKw3mVwWSmGBf9%2FB1rCOJ%2B2Ek|20190309|3|P2|01|04|1|0|||||||||||有|有|4||O0M090|OM9|0|0|null","7y4dlFjVUCPXz1Fg7LgkQSGFjFAO1Po9fFRKuI4Q3Z9y%2B6lTS4jzNZOPchvHPdRBILApTMo3Z1g9%0AkX%2F8ETwV9zwxtNuUjcRVcndLDHIG60xlqzn8L7fzx2y1N10x8TRLfepldMfLpK%2FwywMttxGWjzAZ%0Ay246Fhg1MgH%2BJU0Q%2F%2FKAT%2FMKDmngI6hzQj5PoQ5CwN540NMVZEoSge6Podm0Eu%2FQWzLmH0ekty2y%0ABm7fRxfJhAAhJXYv3HIKx8SNE%2FU%2FxBCTlf2lZTKjgNJ5LnzMyL86iaoNQ46m2CvKE%2BWloQ3l3END%0A|预订|240000G1551Y|G155|VNP|AOH|VNP|AOH|15:45|21:41|05:56|Y|qUCJ%2F3zmqdC8uWjPGa6TYtiQ0zXxF814qhaoV9fmvs6B0EaL|20190309|3|P4|01|10|1|0|||||||||||有|有|11||O0M090|OM9|1|0|null","2NNK9cuypuSP278eD4McN%2FWbOts42OR0Tl0fvsLHJ58CyvMxEiBd%2FryVfm8stRPeSgNQj35HMq%2BQ%0AiXD5eIAZNqBldIlvtFAB182AMOctr8LJP7YdwmTnPR%2BjfK4YGXo%2BAhyY4Z7NmTqohD569v%2Ff6yWH%0A9j%2Bo60TuRU5T9ecmslSejW6timcRxxRlASu9c3z1rAaQM5q8WUICKh%2FbbMXf%2BGmAqquClmFSFvse%0AEDaHnXB1OXuqDMSOLYLCipJgSE%2BLPBCt0ah6mFvlDELCf7m3ZrtTE1c0BBRwvPf23jyVmX93sMlB%0A|预订|240000G1470G|G147|VNP|AOH|VNP|AOH|15:50|22:00|06:10|Y|14zQKSXnFjQpuLb4DY0ENEcTQQ1pvE6UofWXC0YPh9vwgHqI|20190309|3|P3|01|12|1|0|||||||||||有|有|12||O0M090|OM9|0|0|null","aL1TfJ5blkRE2W02mE81%2BasqLRYmtUFqY6ASR%2BWAfBUzfUCHJVnX5ZE52iMqjRNrkym4byjwdUSJ%0A6frSlDIc%2BF6yMBW17gjNU2Z7ptyPea%2BGdgrb52V2ZdczWPPIapDVKtQ34spzcuHr7EJRo7fK0YVe%0AjVHEBr21J5u%2BfZjYD4gp8bMSWLSBPzQ%2FojwsLrAEHr%2FZMXsYYk9mhhzHURl6xz2somfaZVpeMSpS%0ARlFwTUwZ7htCWZHFk5TXzo3sTCug3VG7Hl8SXjHMbtWlF1f1n1bqDcFsu%2FKwt5OPzwZjug4XxYMx%0A|预订|240000G16903|G169|VNP|AOH|VNP|AOH|16:40|22:35|05:55|Y|k620bI6rFPNp86WspRFbrxhRpvX5RvWX6mauornKrN%2Fz9CZ6|20190309|3|P4|01|10|1|0|||||||||||有|有|有||O0M090|OM9|1|0|null","sNUjTO1NjdbrpOg2gp%2BnG1xlSo1jrBDPzh%2BaS%2F8Wym8g43o1dXRS2m0fPzk%2BTJ92b1eZfpkavhRy%0A24tqIlTifhBHfiuHQTqEoMDPmYib9XZyO1cxuf7NQr4FFZ%2BIGQ7hXWt%2FogsGw6M0BaE%2BGYcElv3b%0APy2pDLmjWGSMRVu7XYb7Hd8vK%2B6Ybmxl3hfBgQG0gG6bKPFc6M8Vgxh5078rVKZgk4NPZVMh2zgc%0Avt%2Bkby33QtTF9eZ%2Br8bbnxkQrg9IsUzexyirnKUSvhwz2MZePZ2lHoMunsRcR%2BhRrfjRaNqNMKI0%0A|预订|240000G1512O|G151|VNP|AOH|VNP|AOH|16:45|23:02|06:17|Y|tQcPja5YW%2B4SMR5zy3HlPfSZXJgtQ80zZwQsgBLs1DycbAV6|20190309|3|P2|01|13|1|0|||||||||||有|有|15||O090M0|O9M|1|0|null","kU3Oq6NUrzl4XudJVFwj9av0M7m%2Bwm4v%2BgXydTMOCnDxuo5ozdGFjba5f6BVVLOZkSkQdUs2ZaND%0ABEaeyYcbn%2FkOm8NTsySNWmeoCdcC0VZ%2F2GYVNS50rzz5oCTZxFpdQZ3c%2F5OL56XfBIQ5pyasaAjU%0AUfNMFOjieoFTlmO7AzT5cGnSbt1FYSjPdzmyKGbpL3ML3EXNnRkvoqp%2BcUlRcJYTVni%2BHFn1dRzN%0A6cKHpoIJNnjvcAkCTWWAVkj2rHzuxA5WJ%2FRWuQ00NhcdIZYR97lsxoBjxsz%2F2RVPAVPN%2Fq3rcph%2B%0A|预订|2400000G130A|G13|VNP|AOH|VNP|AOH|17:00|21:36|04:36|Y|e4NZbcteXsulZLIpbBVWnIZb%2Fs6ej3rXBh4h20vxq0ziIUYW|20190309|3|P3|01|05|1|0|||||||||||有|有|6||O0M090|OM9|0|0|null","VZNvokZEsIJIDCkoGJoLdtqDS9sgZIUWKXnVPXyzZEAA2k5n3HdfmHVQuTrAcXpAX2egWYYBiHg8%0ANFp%2FAkx0JFR9fuJxo2abpFFFI8HbAjpyv6hhecxsQx3p2ZXXi7Dj0ghn0e7UdXo12EfN9Zgss8QV%0AoPjvFT3un7KCZIHKlkZDjSDxOmmtER6wkXHsfgK1%2F0s59sl5MxcS%2FD8JXEt32pbg3Obdy%2FSsQBJh%0ASjZNBUaqy3VH2kJLR2siiP7saAzKW0wS8ZzzmV1EUB93VwoIigInyLZmc36hqbHU29JdnkWpCwo7%0A|预订|240000G1530I|G153|VNP|AOH|VNP|AOH|17:16|23:11|05:55|Y|IjYiKfTpux5ZGuq8nctz227yi3qEyqIUduIZz5h4ulmtZO3j|20190309|3|P3|01|11|1|0|||||||||||有|有|12||O0M090|OM9|1|0|null","ctArE2k%2FocUM4gDsLRxoyyXInriHGIdJdNMKS1r7HX%2BqHfsdmqR1WB%2BltVGm9DvHDpMe12sKwpVv%0A3v8HqqVFqM1gx9D1Rft86h4pR4%2FZ%2Bt6ovqGxP5aFe9hPHsttxlMrLktr2dJzDgyCx7Abf%2Fbbtar2%0A1vKgxO9LL2ExOsMGR5NR7NFUwwu2FMdLLfhuACnPouuIN4ooZ5nUIQ90YsTbMj24xCLXbP7JK7MZ%0AicsT0msdC%2BZkIF%2BqIjthFA1MPxu%2F5asEqXDOwBiAPNweup7GMXvSuJ9AFGMk3EIhqcM2CSGkhxLT%0A|预订|240000G1570O|G157|VNP|AOH|VNP|AOH|17:36|23:40|06:04|Y|9%2BaLYWexazLLEvkVe5W42qHd45zRmbJiMUwmYQQwJ6Bv4zB2|20190309|3|P3|01|10|1|0|||||||||||有|有|12||O0M090|OM9|1|0|null","KT5GrQ1Ed5DGcunh2MfdKNQZujUx8KzEIq7%2BJNS4bwPGZB3ld0NNLZJOUYggPmmpAyLlfbuClr9t%0AGb5gyftYXjRuobxhnOr2IFbmjt0jvEoxXBi77vgz1mMJtJUIIgqiL2meMcYtgSRI1SVGbeC7%2FU%2F8%0AxN6YW0cpPj2X6Sew5eFnrfi5PzSgi95MivdOxUVEKM%2BomqOGSEZoDCMsPysn5kG%2BlUOp61SKUbY5%0ADS1ylv1v38d%2ByAi55UoRVmIVByjd0JhNqeEnySzZ2%2FoUKTaWEGsLEazi%2BPkmLRUSYh%2F2Xk4%3D|预订|2400000G1500|G15|VNP|SHH|VNP|SHH|18:00|22:43|04:43|Y|PSJ4N%2BUkSQ5e%2F%2Fn%2Bvw8v6DiJpuc5bF900MOqmJ%2BIT3U2M3rs|20190309|3|P4|01|05|1|0|||||||||||有|有|7||O0M090|OM9|1|0|null","CJsJN6MAtvVTHmlWxTYtP21FwLT3Tj1Ny6naecNiqRrhJzZ3ZdXh1LCmJJB2goqUuhQUDHmYGkM0%0AwS51PiK6nZpUIxgDGx1Ui7HHx72gHlD8hkDBEbrK2GNaxMUEh0%2FpnPrwK2c3ISPpx5%2F%2FLg27V2T%2F%0AWyQkNq3a0LI60BQ7KIGuqPKwu1Cvg%2BJfTtC%2Baf7rJxnfcchDg%2BDQTQTJj%2FCvShJmvDDo54ox%2BHJG%0AawZJ%2BQDgoLj7s4fhjVKyUjL4wjpe7L6i9a8FUTWsrp3PDYttWd7rKHCCZdvKpwoy4jiySMDin485%0Aor4L5w%3D%3D|预订|330000Z2840E|Z281|BTC|HZH|BJP|SNH|18:58|09:53|14:55|Y|TpU%2FiV206rIoRtKSCIJjmIsXTVVfCuZLRg9Vj%2F8isKNvigWQy9euh%2Fhhjgo%3D|20190309|3|C1|08|16|0|0||||无|||有||无|无|||||10401030|1413|0|0|null","6isNYEXiLmxYSwWSq1fw4EwSWihwOvV3aOUw2hQDDBhehCRQUm01vhcMVhUMvvo2fSDhCbzw0ZKu%0ASSh%2FCJ7QeFzl%2BR%2Bd%2BS4NpyhwqdulbY0CPtwjdwvtaN35bJq1D1WM1s1HUrIO%2B0hH9gzUZQXdWJmX%0AiDl6IkCaliaQ7UEPdFW%2BUdqErYNCXXayQbRAZUSxt%2B5hpJViQ%2Bi0Zk96EJtw8g40aYafk6K4Yo3L%0AJIIRykrODW1RNpxn41jbV1gD%2FuFSyLacJZVkGCjgmTBKMai8dSra13ihZm7a4nxd91fHmaMLtg4v%0A|预订|2400000G170B|G17|VNP|AOH|VNP|AOH|19:00|23:18|04:18|Y|yzyMHfPllCk3ldU2pI64eAjxmtXQrszOvjt3d%2BKzhsHttVcr|20190309|3|P3|01|03|1|0|||||||||||有|有|9||O0M090|OM9|1|0|null","s41mzMzbHn8yyZxmo%2Fuobz5MtVqwKDfWb6Nv0WTbeE8dztvImlsJmBvDokooSVsYNcm5BdnOQSBG%0AzVjGidjQfYvcoq%2Bn8CuCFehQm3HO5leeWg4vS%2Frfc2hZkB1KUmktfQdrpvuwamdd5YeHiTjatMas%0Ap3totRuD6ygyrKnGzF8VBexMkvz%2BDRQKYegNev7lwwcamUjgqY2%2BVymxEfItGpK0QKXAx6xpUt02%0AoMYx%2FpsufjMcj2vJa7BEBh4BfnX%2BGvFq%2FNBn3urWnk%2FMNyBJuWLD%2F6v2EAV9mP%2B%2B1EIHxuw%3D|预订|2400000G2109|G21|VNP|SHH|VNP|SHH|19:08|23:36|04:28|Y|lpQ4AmXxbwY8iKy98Qtqj7IOuuk%2F5tLGJST%2F5gRJNTlepwB0|20190309|3|P2|01|04|1|0|||||||||||有|有|10||O0M090|OM9|1|0|null","%2FTu%2FupopKprGEWfE45HogtABdJTb9Rs%2ForOXkfMou5Lzs1OBJYLwImOaQFlpFBY3WejT6QeSf869%0ABe6XS24zdmxzYYhXx3lgTvO8r%2F0TCCNFx9BMFKN15ie7txXtboDBQSkSrszGPuNQkHhEyM10liYf%0AlMkvY0w2xE%2FnuPM%2Bo9xn2UUR1IsBwL9BWyfHopev1Gyq5bSTcZHJkWcAIJ%2BoAfue6XvjA4gOmch1%0AKtW7ojqRQ2QhHQeKajGSGHsX4mXLGA0plaW9fRFvGhGjGj9rvfop9K4qaTEsC%2BlqUwnJfQIATTh%2F%0AVWIv5A%3D%3D|预订|240000D70100|D701|BJP|SHH|BJP|SHH|19:22|07:25|12:03|Y|DPG%2FwxHn2C99ElugZD5YcgeozjZbbUd%2Ffc7YZXdX5jw2yUK9LuRiDbMmmX8%3D|20190309|3|P2|01|05|0|0||||有|||有||有||有||||O0J0O0I0|OJOI|0|0|null","YQ5A74cil5TgeCqeB34Z49O73tm%2FvgDrjMylNVplCfpIhaJP50DN%2BrUxWYJaQu5ea0P8H%2FHMxYDx%0Ap7mMmKErvmwKc6beKjl1Ci4pd4%2BI0rWZBHXPhS58mneLOREEpmq9eJhXmk7FwUNpSaP4EYbX0%2BVW%0ALffpY7ERFYZIziRF1UuwH7RHPyK9g2u18fl9ECwGyr10nyfUckj6Cg36nSF8k2ZffhcMQXexbfGR%0AbncPeelvczTNkiY50VG9oRafuUkRUgfzmzOND%2FjZPf0O0ktrWb%2F%2FF9rPaJ%2Fva7XC9sYfSY3n7U0R%0AgdGuiQ%3D%3D|预订|240000D70900|D709|VNP|SHH|VNP|SHH|19:42|07:42|12:00|Y|7SYr4GqoxaFkZQnem8dtkfYf7drluqMl61MM2MP5L5GExrYbwsswX8gaFjQ%3D|20190309|3|P4|01|04|1|0||||有|||有||有||有||||O0J0O0I0|OJOI|0|0|null","akJ3HQHu89juEYtXc6PTJFLq762TfNHNbb2mTN%2Fz3SPVM3R5wVxb6LFoQ4mqtOtiCwHlj0Mx6STz%0AtmR3prLpTni62cafck62BbRVXuTYDnooBkgvkMoTVUjFwQ78B0KnHJ92euPjILhagn9W%2FyEe4C%2FK%0Azpz6DBx5K%2FSVRq4whWaU94pnGZ2DZzchJ5kluIfhP6u%2B0az7ByO%2By8mS671g8yUlxSkNo8I6CQ2M%0AFzesICAk8dBqvmb9d5g3T5J1KojpgzDopSFItG538zJH3367u%2Bc6P3i7zS0v8Ck19%2BrIOT9WsoMB%0ABdYPa6PSQ0gwyZpy|预订|240000T10912|T109|BJP|SHH|BJP|SHH|20:05|11:00|14:55|Y|vMZWm0nUh1DRyvRE1Drf%2BnCtVZDRi5dHv4rlEDCAFKxABincqDhQRSykgs8iVwQF85iovbPZz7Y%3D|20190309|3|P4|01|10|0|0||11||15|||有||无|有|||||1040601030|14613|0|0|null","nzgE2ukFUiexSC8Jqzl%2BtK7myrzyGfrpn3Ta7%2BppPf3mMSinEs5sSkKq%2BA%2BChxh6vyWWUa5rCdm8%0AVacG5DUha1zSQeeFdZSrdcO9kQ%2FPzWhgFuCsXmdflisEaR148qFCXWxckJ4zOZ4rSsC2l9gYfD3M%0AQYgpdzk9Ao1NGWYvKBKQSelMi48cPk7%2FeHrTnhTnefaYtsbvwnHQNT3E2vEmGgPwyN9PQ%2FkrIXxu%0A8P7BFcx7SGM6bLLJLeFVPtwD%2FSn0DFMx6vXjvv4HdjA8qb4BjNsUIdl6lRVPWm80z7MrG7VRP39V%0A30Pn5g%3D%3D|预订|240000D70500|D705|BJP|SHH|BJP|SHH|21:21|09:20|11:59|Y|4vtYauU83XhQjSTfIEzfPzpMiYWQSFXBmWaR0skhGvzlfu5II%2BbGpuqVsbU%3D|20190309|3|P4|01|04|0|0||||有|||有||有||有||||O0J0O0I0|OJOI|1|0|null"]},"httpstatus":200,"messages":"","status":true}
    

    大家会发现控制台输出的结果就是车票的信息,只是还没有进过加工的信息。先看一下这段代码,首先导入requests库,然后向URL发送一个get请求,得到了一个response响应,然后我们打印出响应的内容。
    先来将一下这个请求的URL地址怎么来的,我们以谷歌浏览器为例子,打开到12306车票查询界面,然后打开开发者工具
    在这里插入图片描述
    将开发者工具跳到Network页面,然后点击查询按钮
    在这里插入图片描述
    在页面会出现发出的请求和响应内容,查看第一个详细信息
    在这里插入图片描述
    在这里插入图片描述
    这里可以看见请求的URL,请求的方法
    在这里插入图片描述
    点击Preview,你在下面会看见和刚才运行代码控制台打印出来的内容一样,其实我们得到的响应内容就是得到的这里的信息,我们在回过头来分析一下刚才的代码,我们仔细看一下请求的URL:

    https://kyfw.12306.cn/otn/leftTicket/queryX?leftTicketDTO.train_date=2019-03-09&leftTicketDTO.from_station=BJP&leftTicketDTO.to_station=SHH&purpose_codes=ADULT

    train_date=2019-03-09 这个就是我们查询车票的时间
    from_station=BJP 这个就是代表起始站
    left_station=SHH 这个就是代表终点站
    分析完URL之后,我们就可以改变其中的参数来爬取不同车次的信息。
    requests向目标URL发送请求的时候有多种请求方式,我们刚才获取到的详细信息是一个get请求,就可以用requests.get()这个方法来发送请求,然后用response接受请求之后的响应。response.text是得到响应的文本内容,response还可以将响应的内容转化为json格式。

    response.json()
    

    接下来我们对得到的数据进行解析

    result = response.json()['data']['result']
    
    for i in range(len(result)):
        checi = result[i].split("|")[3]  # 车次信息
        start_time = result[i].split("|")[8]  # 出发时间
        end_time = result[i].split("|")[9]  # 到达时间
        need_time = result[i].split("|")[10]  # 历时
        normal_seat = result[i].split("|")[32]  # 商务座余票
        first_seat = result[i].split("|")[31]  # 一等座余票
        second_seat = result[i].split("|")[30]  # 二等座余票
        print(checi, start_time, end_time, need_time, normal_seat, first_seat, second_seat)
    

    运行后会发现控制台打印出了解析过后的信息,然后我们就可以得到这些信息了,这就是一个简单的爬取12306的信息的爬虫,先就到这里了,大家需要注意一下,有的网站做了反爬虫,有时候会封你的ip,例如某外卖,下次说一下爬虫框架scrapy。

    展开全文
  • 爬虫学习

    2019-01-24 11:11:32
     网络爬虫介绍 网络爬虫(Web crawler),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本    什么是网络爬虫 在大数据时代,信息的采集是一项重要的工作,而互联网中的数据是海量的,如果单纯靠...

     

       网络爬虫介绍

    网络爬虫(Web crawler),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本

     

      什么是网络爬虫

    在大数据时代,信息的采集是一项重要的工作,而互联网中的数据是海量的,如果单纯靠人力进行信息采集,不仅低效繁琐,搜集的成本也会提高。如何自动高效地获取互联网中我们感兴趣的信息并为我们所用是一个重要的问题,而爬虫技术就是为了解决这些问题而生的。

     

    网络爬虫(Web crawler)也叫做网络机器人,可以代替人们自动地在互联网中进行数据信息的采集与整理。它是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本,可以自动采集所有其能够访问到的页面内容,以获取或更新这些网站的内容和检索方式。

     

    从功能上来讲,爬虫一般分为数据采集,处理,储存三个部分。爬虫从一个或若干初始网页的URL开始,获得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件。

     

    我们感兴趣的信息分为不同的类型:如果只是做搜索引擎,那么感兴趣的信息就是互联网中尽可能多的高质量网页;如果要获取某一垂直领域的数据或者有明确的检索需求,那么感兴趣的信息就是根据我们的检索和需求所定位的这些信息,此时,需要过滤掉一些无用信息。前者我们称为通用网络爬虫,后者我们称为聚焦网络爬虫。

     

    入门程序

    网络爬虫说就是用程序帮助我们访问网络上的资源,我们一直以来都是使用HTTP协议访问互联网的网页,我们也需要编写程序,使用同样的协议访问网页。

    这里我们使用Java的HTTP协议库 HttpComponents这个技术,来实现抓取网页数据。我们要抓取的是开源中国的HttpComponents页面的文章。

    1 创建工程

    创建工程itcast-crawler

    加入以下依赖:

       <dependencies>

           <!-- httpcomponents依赖,包含HttpClient -->

           <dependency>

               <groupId>org.apache.httpcomponents</groupId>

               <artifactId>httpasyncclient</artifactId>

               <version>4.1.3</version>

           </dependency>

     

           <!-- 日志 -->

           <dependency>

               <groupId>org.slf4j</groupId>

               <artifactId>slf4j-log4j12</artifactId>

               <version>1.7.25</version>

           </dependency>

     

           <!-- 工具类 -->

           <dependency>

               <groupId>org.apache.commons</groupId>

               <artifactId>commons-lang3</artifactId>

               <version>3.3.2</version>

           </dependency>

        </dependencies>

     

    加入log4j.properties

    log4j.rootLogger=DEBUG,A1

    log4j.logger.cn.itcast = DEBUG

     

    log4j.appender.A1=org.apache.log4j.ConsoleAppender

    log4j.appender.A1.layout=org.apache.log4j.PatternLayout

    log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n

     

     

     

     

     

    2. 抓取页面

    案例:访问开源中国的HttpComponents项目的介绍页面

    https://www.oschina.net/p/httpclient

     

    编写以下代码

    // 访问地址

    private static String url = "https://www.oschina.net/p/httpclient";

     

    public static void main(String[] args) throws Exception {

     

        // 创建Httpclient对象

        CloseableHttpClient httpclient = HttpClients.createDefault();

     

        // 创建http GET请求

        HttpGet httpGet = new HttpGet(url);

     

        // 解决开源中国不允许爬虫访问的问题

        httpGet.setHeader("User-Agent", "");

     

        // 执行请求

        CloseableHttpResponse response = httpclient.execute(httpGet);

        // 判断返回状态是否为200

        if (response.getStatusLine().getStatusCode() == 200) {

           String content = EntityUtils.toString(response.getEntity(), "UTF-8");

           // 输出抓取到的页面

           Writer out = new FileWriter(new File("D:/httpclient.html"));

           out.write(content);

           out.close();

        }

    }

     

    3. 解析页面

    我们抓取到页面之后,需要对页面进行解析。我们使用浏览器分析发现在一个div标签里面有这个项目文章的正文。这个div标签的class属性为detail editor-viewer all。需要解析这个页面,获取正文内容

    我们可以使用以下代码解析页面

    // 解析页面,获取需要的内容

    String str = StringUtils.substringBetween(content, "<section class=\"panel-body\" id=\"v-details\">",

           "</section>");

    System.out.println(str);

     

    这里就已经把内容获取到了,这就是一个最简单的爬虫的实现,无非就是抓取数据,分析数据,存储数据。

     

    我们已经获取了需要的内容,但是还有很多html的标签没有解析,这会影响我们对数据的处理,虽然这些标签也可以手动编写逻辑过滤掉,但是会耗费我们大量时间,一般情况我们会使用专门的html解析工具进行解析。

    4.   Jsoup

    jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。

     

    jsoup的主要功能如下:

                1. 从一个URL,文件或字符串中解析HTML;
                2. 使用DOM或CSS选择器来查找、取出数据;
                3. 可操作HTML元素、属性、文本;

     

    先加入Jsoup依赖:

           <!-- jsoup -->

           <dependency>

               <groupId>org.jsoup</groupId>

               <artifactId>jsoup</artifactId>

               <version>1.10.3</version>

           </dependency>

    4.1. jsoup输入

    4.1.1. 输入字符串

    public class HttpClientTest {

     

        // 访问地址

        private static String url = "https://www.oschina.net/p/httpclient";

     

        public static void main(String[] args) throws Exception {

     

           // 创建Httpclient对象

           CloseableHttpClient httpclient = HttpClients.createDefault();

     

           // 创建http GET请求

           HttpGet httpGet = new HttpGet(url);

     

           // 解决开源中国不允许爬虫访问的问题

           httpGet.setHeader("User-Agent", "");

     

           // 执行请求

           CloseableHttpResponse response = httpclient.execute(httpGet);

           // 判断返回状态是否为200

           if (response.getStatusLine().getStatusCode() == 200) {

               String content = EntityUtils.toString(response.getEntity(), "UTF-8");

               // 输出抓取到的页面

               Writer out = new FileWriter(new File("D:/httpclient.html"));

               out.write(content);

               out.close();

     

               // 解析页面,获取需要的内容

               String str = StringUtils.substringBetween(content, "<section class=\"panel-body\" id=\"v-details\">",

                      "</section>");

               System.out.println(str);

     

               // 使用jsoup解析

               System.out.println("------以下jsoup----------");

               // 把内容解析为Document对象

               Document doc = Jsoup.parse(content);

               // 使用dom的方式解析内容

               Elements elements = doc.getElementsByClass("detail editor-viewer all");

               for (Element element : elements) {

                  System.out.println(element.text());

               }

           }

        }

    }

     

    4.1.2. 输入url

    我们也可以使用jsoup直接解析一个url地址。

    public class JsoupTest {

        // 访问地址

        private static String url = "https://www.oschina.net/p/httpclient";

     

        public static void main(String[] args) throws Exception {

           // 创建连接

           Connection conn = Jsoup.connect(url);

           // 设置头信息,解决反爬虫限制

           conn.header("User-Agent", "");

           // 发起get请求,并发结果解析为Document对象

           Document doc = conn.get();

     

           // 获取需要的数据

           Elements elements = doc.getElementsByClass("detail editor-viewer all");

           for (Element element : elements) {

               System.out.println(element.text());

           }

        }

    }

     

    PS:虽然使用Jsoup可以替代HttpClient的作用直接发起请求解析数据,但是企业开发中往往不会这样用,因为实际的爬虫开发过程中,需要使用到多线程,连接池,代理等等技术,而jsoup对这些技术的支持并不是很好,所以jsoup一般仅仅作为Html解析工具使用

     

    4.1.3. 输入文件

    也可以直接解析文件

    // 解析文件为Document对象

    // 第一个参数为文件对象,第二个参数为编码

    Document doc = Jsoup.parse(new File("D:/httpclient.html"), "UTF-8");

     

    // 获取需要的数据

    Elements elements = doc.getElementsByClass("detail editor-viewer all");

    for (Element element : elements) {

        System.out.println(element.text());

    }

     

    4.2. Jsoup解析

    4.2.1. 使用dom方式遍历文档

    查找元素

    getElementById(String id)

    getElementsByTag(String tag)

    getElementsByClass(String className)

    getElementsByAttribute(String key) (and related methods)

    Element siblings: siblingElements(), firstElementSibling(), lastElementSibling(); nextElementSibling(), previousElementSibling()

    Graph: parent(), children(), child(int index)

    元素数据

    attr(String key)获取属性attr(String key, String value)设置属性

    attributes()获取所有属性

    id(), className() and classNames()

    text()获取文本内容text(String value) 设置文本内容

    html()获取元素内HTMLhtml(String value)设置元素内的HTML内容

    outerHtml()获取元素外HTML内容

    data()获取数据内容(例如:script和style标签)

    tag() and tagName()

     

    4.2.2. 使用选择器语法查找元素

    jsoup elements对象支持类似于CSS (或jquery)的选择器语法,来实现非常强大和灵活的查找功能。这个select 方法在Document, Element,或Elements对象中都可以使用。且是上下文相关的,因此可实现指定元素的过滤,或者链式选择访问。

     

    Select方法将返回一个Elements集合,并提供一组方法来抽取和处理结果。

     

    4.2.2.1. Selector选择器概述

    tagname: 通过标签查找元素,比如:a

    ns|tag: 通过标签在命名空间查找元素,比如:可以用 fb|name 语法来查找 <fb:name> 元素

    #id: 通过ID查找元素,比如:#logo

    .class: 通过class名称查找元素,比如:.masthead

    [attribute]: 利用属性查找元素,比如:[href]

    [^attr]: 利用属性名前缀来查找元素,比如:可以用[^data-] 来查找带有HTML5 Dataset属性的元素

    [attr=value]: 利用属性值来查找元素,比如:[width=500]

    [attr^=value], [attr$=value], [attr*=value]: 利用匹配属性值开头、结尾或包含属性值来查找元素,比如:[href*=/path/]

    [attr~=regex]: 利用属性值匹配正则表达式来查找元素,比如: img[src~=(?i)\.(png|jpe?g)]

    *: 这个符号将匹配所有元素

     

    4.2.2.2. Selector选择器组合使用

    el#id: 元素+ID,比如: div#logo

    el.class: 元素+class,比如: div.masthead

    el[attr]: 元素+class,比如: a[href]

    任意组合,比如:a[href].highlight

    ancestor child: 查找某个元素下子元素,比如:可以用.body p 查找在"body"元素下的所有 p元素

    parent > child: 查找某个父元素下的直接子元素,比如:可以用div.content > p 查找 p 元素,也可以用body > * 查找body标签下所有直接子元素

    siblingA + siblingB: 查找在A元素之前第一个同级元素B,比如:div.head + div

    siblingA ~ siblingX: 查找A元素之前的同级X元素,比如:h1 ~ p

    el, el, el:多个选择器组合,查找匹配任一选择器的唯一元素,例如:div.masthead, div.logo

     

    4.2.2.3. 伪选择器selectors

    :lt(n): 查找哪些元素的同级索引值(它的位置在DOM树中是相对于它的父节点)小于n,比如:td:lt(3) 表示小于三列的元素

    :gt(n):查找哪些元素的同级索引值大于n,比如: div p:gt(2)表示哪些div中有包含2个以上的p元素

    :eq(n): 查找哪些元素的同级索引值与n相等,比如:form input:eq(1)表示包含一个input标签的Form元素

    :has(seletor): 查找匹配选择器包含元素的元素,比如:div:has(p)表示哪些div包含了p元素

    :not(selector): 查找与选择器不匹配的元素,比如: div:not(.logo) 表示不包含 class=logo 元素的所有 div 列表

    :contains(text): 查找包含给定文本的元素,搜索不区分大不写,比如: p:contains(jsoup)

    :containsOwn(text): 查找直接包含给定文本的元素

    :matches(regex): 查找哪些元素的文本匹配指定的正则表达式,比如:div:matches((?i)login)

    :matchesOwn(regex): 查找自身包含文本匹配指定正则表达式的元素

     

    5.   爬虫分类

    网络爬虫按照系统结构和实现技术,大致可以分为以下几种类型:通用网络爬虫、聚焦网络爬虫、增量式网络爬虫、深层网络爬虫。 实际的网络爬虫系统通常是几种爬虫技术相结合实现的

    5.1. 通用网络爬虫

    简单的说就是互联网上抓取所有数据。

     

    通用网络爬虫又称全网爬虫(Scalable Web Crawler),爬行对象从一些种子 URL 扩充到整个 Web,主要为门户站点搜索引擎和大型 Web 服务提供商采集数据。

    这类网络爬虫的爬行范围和数量巨大,对于爬行速度和存储空间要求较高,对于爬行页面的顺序要求相对较低,同时由于待刷新的页面太多,通常采用并行工作方式,但需要较长时间才能刷新一次页面。

     

    5.2. 聚焦网络爬虫

    简单的说就是互联网上只抓取某一种数据。

     

    聚焦网络爬虫(Focused Crawler),又称主题网络爬虫(Topical Crawler),是指选择性地爬行那些与预先定义好的主题相关页面的网络爬虫。

    和通用网络爬虫相比,聚焦爬虫只需要爬行与主题相关的页面,极大地节省了硬件和网络资源,保存的页面也由于数量少而更新快,还可以很好地满足一些特定人群对特定领域信息的需求 。

     

    5.3. 增量式网络爬虫

    简单的说就是互联网上只抓取刚刚更新的数据。

     

    增量式网络爬虫(Incremental Web Crawler)是 指 对 已 下 载 网 页 采 取 增量式更新和只爬行新产生的或者已经发生变化网页的爬虫,它能够在一定程度上保证所爬行的页面是尽可能新的页面。

    和周期性爬行和刷新页面的网络爬虫相比,增量式爬虫只会在需要的时候爬行新产生或发生更新的页面 ,并不重新下载没有发生变化的页面,可有效减少数据下载量,及时更新已爬行的网页,减小时间和空间上的耗费,但是增加了爬行算法的复杂度和实现难度。

     

    5.4. Deep Web 爬虫

    Web 页面按存在方式可以分为表层网页(Surface Web)和深层网页(Deep Web,也称 Invisible Web Pages 或 Hidden Web)。

    表层网页是指传统搜索引擎可以索引的页面,以超链接可以到达的静态网页为主构成的 Web 页面。

    Deep Web 是那些大部分内容不能通过静态链接获取的、隐藏在搜索表单后的,只有用户提交一些关键词才能获得的 Web 页面。

     

    6.   案例介绍

    前面介绍了几种爬虫的分类,这里我们使用聚焦网络爬虫,抓取京东上面的商品数据。

    真正的开发需要不断的爬去商品数据,更新商品数据信息,所以要用增量式网络爬虫。我们这里使用定时爬去的方式来实现,所以还需要使用到定时任务Quartz

    6.1. 环境准备

    使用技术:jdk8+SpringBoot+HttpClient+Jsoup+Quartz+MyBatis

     

    6.2. 搭建工程

    创建Maven工程

    工程需要集成SpringBoot父

        <parent>

           <groupId>org.springframework.boot</groupId>

           <artifactId>spring-boot-starter-parent</artifactId>

           <version>1.5.8.RELEASE</version>

        </parent>

     

    6.2.1. 加入依赖

    设置jdk为1.7,并加入相关依赖

        <properties>

           <java.version>1.7</java.version>

        </properties>

     

        <dependencies>

           <!-- spring相关包 -->

           <dependency>

               <groupId>org.springframework.boot</groupId>

               <artifactId>spring-boot-starter-web</artifactId>

           </dependency>

     

           <!-- 配置测试启动器 -->

           <dependency>

               <groupId>org.springframework.boot</groupId>

               <artifactId>spring-boot-starter-test</artifactId>

               <scope>test</scope>

           </dependency>

     

           <!-- MyBatis启动器 -->

           <dependency>

               <groupId>org.mybatis.spring.boot</groupId>

               <artifactId>mybatis-spring-boot-starter</artifactId>

               <version>1.1.1</version>

           </dependency>

           <!-- MySQL连接驱动 -->

           <dependency>

               <groupId>mysql</groupId>

               <artifactId>mysql-connector-java</artifactId>

           </dependency>

     

           <!-- 通用Mapper -->

           <dependency>

               <groupId>com.github.abel533</groupId>

               <artifactId>mapper</artifactId>

               <version>2.3.4</version>

           </dependency>

           <!-- 分页助手 -->

           <dependency>

               <groupId>com.github.pagehelper</groupId>

               <artifactId>pagehelper</artifactId>

               <version>3.7.5</version>

           </dependency>

           <dependency>

               <groupId>com.github.jsqlparser</groupId>

               <artifactId>jsqlparser</artifactId>

               <version>0.9.1</version>

           </dependency>

     

           <!-- 工具类 -->

           <dependency>

               <groupId>org.apache.commons</groupId>

               <artifactId>commons-lang3</artifactId>

               <version>3.3.2</version>

           </dependency>

     

           <!-- 定时任务Quartz -->

           <dependency>

               <groupId>org.quartz-scheduler</groupId>

               <artifactId>quartz</artifactId>

               <version>2.2.1</version>

           </dependency>

           <dependency>

               <groupId>org.springframework</groupId>

               <artifactId>spring-context-support</artifactId>

           </dependency>

     

           <!-- httpcomponents依赖,包含HttpClient -->

           <dependency>

               <groupId>org.apache.httpcomponents</groupId>

               <artifactId>httpasyncclient</artifactId>

           </dependency>

     

           <!-- jsoup -->

           <dependency>

               <groupId>org.jsoup</groupId>

               <artifactId>jsoup</artifactId>

               <version>1.10.3</version>

           </dependency>

     

        </dependencies>

     

    6.2.2. 加入配置

    在src/main/resources路径下加入以下两个配置文件

     

    加入application.properties

    #日志

    logging.level.org.mybatis=DEBUG

    logging.level.cn.itcast=DEBUG

     

    #spring集成Mybatis环境

    #pojo别名扫描包

    mybatis.type-aliases-package=cn.itcast.crawler.pojo

    #加载Mybatis核心配置文件

    mybatis.mapper-locations=classpath:mapper/*Mapper.xml

    mybatis.config-location=classpath:SqlMapConfig.xml

     

    #DBConfiguration:

    spring.datasource.driverClassName=com.mysql.jdbc.Driver

    spring.datasource.url=jdbc:mysql://127.0.0.1:3306/crawler?useUnicode=true&characterEncoding=utf8

    spring.datasource.username=root

    spring.datasource.password=root

     

    加入SqlMapConfig.xml

    <?xml version="1.0" encoding="UTF-8" ?>

    <!DOCTYPE configuration

      PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

      "http://mybatis.org/dtd/mybatis-3-config.dtd">

    <configuration>

     

        <plugins>

           <!-- 分页助手 -->

           <plugin interceptor="com.github.pagehelper.PageHelper">

               <property name="dialect" value="mysql" />

               <!-- 该参数默认为false -->

               <!-- 设置为true时,使用RowBounds分页会进行count查询 -->

               <property name="rowBoundsWithCount" value="true" />

           </plugin>

     

           <!-- 通用Mapper -->

           <plugin interceptor="com.github.abel533.mapperhelper.MapperInterceptor">

               <!--主键自增回写方法,默认值MYSQL,详细说明请看文档 -->

               <property name="IDENTITY" value="MYSQL" />

               <!--通用Mapper接口,多个通用接口用逗号隔开 -->

               <property name="mappers" value="com.github.abel533.mapper.Mapper" />

            </plugin>

        </plugins>

    </configuration>

     

    6.2.3. 编写引导类

    @SpringBootApplication

    public class Application {

     

        public static void main(String[] args) {

           SpringApplication.run(Application.class, args);

        }

     

    }

     

     

    6.2.4. 准备数据库表

    在MySQL中执行以下语句

     

    创建商品类目url表

    CREATE TABLE `url_item_cat` (

      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '类目ID',

      `url` varchar(1000) DEFAULT NULL COMMENT '类目URL',

      `name` varchar(20) DEFAULT NULL COMMENT '类目名称',

      `created` datetime DEFAULT NULL COMMENT '创建时间',

      `updated` datetime DEFAULT NULL COMMENT '创建时间',

      PRIMARY KEY (`id`),

      KEY `sort_order` (`name`)

    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='商品类目';

     

    创建商品详url表

    CREATE TABLE `url_item` (

      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '类目ID',

      `cat_id` bigint(20) DEFAULT NULL,

      `url` varchar(100) DEFAULT NULL COMMENT '类目URL',

      `status` tinyint(1) DEFAULT '0' COMMENT '状态。可选值:0(未下载),1(已下载)',

      `created` datetime DEFAULT NULL COMMENT '创建时间',

      `updated` datetime DEFAULT NULL COMMENT '创建时间',

      PRIMARY KEY (`id`),

      KEY `parent_id` (`status`) USING BTREE

    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='商品类目';

     

    创建商品表

    CREATE TABLE `item` (

      `id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '商品id,同时也是商品编号',

      `title` varchar(100) NOT NULL COMMENT '商品标题',

      `sell_point` varchar(150) DEFAULT NULL COMMENT '商品卖点',

      `price` bigint(20) NOT NULL COMMENT '商品价格,单位为:分',

      `num` int(10) NOT NULL COMMENT '库存数量',

      `barcode` varchar(30) DEFAULT NULL COMMENT '商品条形码',

      `image` varchar(5000) DEFAULT NULL COMMENT '商品图片',

      `cid` bigint(10) NOT NULL COMMENT '所属类目,叶子类目',

      `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '商品状态,1-正常,2-下架,3-删除',

      `created` datetime NOT NULL COMMENT '创建时间',

      `updated` datetime NOT NULL COMMENT '更新时间',

      PRIMARY KEY (`id`),

      KEY `cid` (`cid`),

      KEY `status` (`status`),

      KEY `updated` (`updated`)

    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='商品表';

     

    6.3. 抓取流程分析

    获取基础数据:

    1. 获取京东商城所有商品的分类url
    2. 保存到数据库

     

    抓取商品数据:

    1. 分析商品分类,获取所有的商品详情url
    2. URL管理器处理URL,作用:

    1). 添加新的待爬url

    2). 判断待添加url是否存在

    3). 判断是否还有待爬url

    4). 获取待爬url

    5). 修改待爬url为已爬url

    1. 网页下载器获取待爬url
    2. 网页解析器解析页面获取商品数据
    3. 保存商品数据到数据库中

     

    7.   开发准备

    7.1. 创建pojo

    复制资料中的pojo到工程中

     

    7.2. 创建Mapper

    创建UrlItemCatMapper

    @org.apache.ibatis.annotations.Mapper

    public interface UrlItemCatMapper extends Mapper<UrlItemCat> {

    }

     

    创建UrlItemMapper

    @org.apache.ibatis.annotations.Mapper

    public interface UrlItemMapper extends Mapper<UrlItem> {

    }

     

    创建ItemMapper

    @org.apache.ibatis.annotations.Mapper

    public interface ItemMapper extends Mapper<Item> {

    }

     

    7.3. 创建Service

     

    7.4. 初始化HttpClient

    编写HttpClient连接池管理器

    @Configuration

    public class HttpClientCfg {

     

        @Bean

        public PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {

           // 创建连接池管理器

           PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();

     

           // 设置最大连接数

           cm.setMaxTotal(200);

           // 设置每个主机地址的并发数

           cm.setDefaultMaxPerRoute(10);

     

           return cm;

        }

     

    }

     

    编写HttpClientService

    public interface HttpClientService {

     

        /**

         * 发起get请求,获取html页面

         *

         * @param url

         * @return

         */

        public String getHtml(String url);

     

        /**

         * 下载图片

         *

         * @param url

         */

        public void getPic(String url);

    }

     

    编写

    @Service

    public class HttpClientServiceImpl implements HttpClientService {

     

        @Autowired

        private PoolingHttpClientConnectionManager cm;

     

        @Override

        public String getHtml(String url) {

           // 使用连接池管理器获取连接

           CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();

     

           // 声明get请求

           // url去空格

           url = StringUtils.replace(url, " ", "");

           HttpGet httpGet = new HttpGet(url);

     

           CloseableHttpResponse response = null;

           try {

               // 发起请求

               response = httpClient.execute(httpGet);

     

               // 判断请求是否结果是否为200

               if (response.getStatusLine().getStatusCode() == 200) {

                  // 如果是200,返回响应体内容

                  String html = EntityUtils.toString(response.getEntity(), "UTF-8");

     

                  return html;

               }

     

           } catch (IOException e) {

               e.printStackTrace();

           } finally {

               // 释放连接

               if (response != null) {

                  try {

                      response.close();

                  } catch (IOException e) {

                      e.printStackTrace();

                  }

               }

           }

     

           return null;

        }

     

        @Override

        public void getPic(String url) {

           // 使用连接池管理器获取连接

           CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();

     

           // 声明get请求

           HttpGet httpGet = new HttpGet(url);

     

           CloseableHttpResponse response = null;

           try {

               // 发起请求

               response = httpClient.execute(httpGet);

     

               // 判断请求是否结果是否为200

               if (response.getStatusLine().getStatusCode() == 200) {

                  // 如果是200,下载图片到本地

                  InputStream inputStream = response.getEntity().getContent();

                  String picName = StringUtils.substringAfterLast(url, "/");

                  FileOutputStream fos = new FileOutputStream(new File("D:/pic/" + picName));

                  response.getEntity().writeTo(fos);

     

               }

     

           } catch (IOException e) {

               e.printStackTrace();

           } finally {

               // 释放连接

               if (response != null) {

                  try {

                      response.close();

                  } catch (IOException e) {

                      e.printStackTrace();

                  }

               }

           }

        }

     

    }

     

    7.5. 获取商品类目url

    查看京东首页,左侧有一个商品分类栏,分析发现,其实是异步加载的json数据,获取json数据并解析,即可拿到所有的商品分类url

     

    编写测试类,获取京东所有商品分类的url

    @RunWith(SpringJUnit4ClassRunner.class)

    @SpringBootTest(classes = Application.class)

    public class ItemCatUrl {

     

        @Autowired

        private UrlItemCatMapper urlItemCatMapper;

     

        // 商品类目访问地址

        private String jdURL = "https://dc.3.cn/category/get";

     

        // 声明json工具类

        private static final ObjectMapper MAPPER = new ObjectMapper();

     

        @Test

        public void testItemCatUrl() throws Exception {

           CloseableHttpClient httpClient = HttpClients.createDefault();

     

           HttpGet httpGet = new HttpGet(jdURL);

     

           CloseableHttpResponse response = httpClient.execute(httpGet);

     

           // 判断请求是否成功

           if (response.getStatusLine().getStatusCode() == 200) {

               // 获取请求返回值

               String json = EntityUtils.toString(response.getEntity(), "GBK");

               // System.out.println(json);

     

               // 解析返回数据

               JsonNode jsonNode = MAPPER.readTree(json);

     

               // 获取一级类目

               ArrayNode array1 = (ArrayNode) jsonNode.get("data");

               for (JsonNode node1 : array1) {

                  node1 = node1.get("s").elements().next();

                  System.out.println(node1.get("n").asText());

                  // 获取二级类目

                  ArrayNode array2 = (ArrayNode) node1.get("s");

                  for (JsonNode node2 : array2) {

                      System.out.println(node2.get("n").asText());

                      // 获取三级类目

                      ArrayNode array3 = (ArrayNode) node2.get("s");

                      for (JsonNode node3 : array3) {

                         // 解析数据

                         String str = node3.get("n").asText();

     

                         // 获取商品分类url

                         String url = "";

                         // 如果url包含域名,则拼接协议

                         if (StringUtils.contains(str, "jd.com")) {

                             url = "https://" + StringUtils.substringBefore(node3.get("n").asText(), "|");

                         } else {

                             // 如果url不包含域名,拼接协议加域名

                             url = "https://list.jd.com/list.html?cat=" + StringUtils.substringBefore(node3.get("n").asText(), "|");

                         }

                         // 保存商品分类url

                         UrlItemCat urlItemCat = new UrlItemCat();

                          urlItemCat.setName(StringUtils.substringBetween(str, "|", "||"));

                         urlItemCat.setUrl(url);

                         urlItemCat.setCreated(new Date());

                          urlItemCat.setUpdated(urlItemCat.getCreated());

                          this.urlItemCatMapper.insert(urlItemCat);

     

                      }

                  }

     

               }

           }

     

        }

     

    }

     

    7.6. 去重过滤器

    在使用网络爬虫过程中,去重是一个不可避免的问题,这里需要对将要爬取得商品详情url进行去重操作

     

    传统的去重,可以使用Map或者Set集合,或者哈希表的方式来实现。在数据量较小的情况下,使用这种方式没有问题,可是当我们需要大量爬去数据的时候,这种方式就存在很大问题。因为会极大的占用内存和系统资源,导致爬虫系统崩溃。

     

    这里将会给大家介绍两种过滤方式:布隆过滤器和redis

     

    7.6.1. 布隆过滤器

    布隆过滤器 (Bloom Filter)是由Burton Howard Bloom于1970年提出,它是一种space efficient的概率型数据结构,用于判断一个元素是否在集合中。在垃圾邮件过滤的黑白名单方法、爬虫(Crawler)的网址判重模块中等等经常被用到。

    哈希表也能用于判断元素是否在集合中,但是布隆过滤器只需要哈希表的1/8或1/4的空间复杂度就能完成同样的问题。布隆过滤器可以插入元素,但不可以删除已有元素。其中的元素越多,误报率越大,但是漏报是不可能的。

     

    布隆过滤器原理

    布隆过滤器需要的是一个位数组(和位图类似)和K个映射函数(和Hash表类似),在初始状态时,对于长度为m的位数组array,它的所有位被置0。

     

    对于有n个元素的集合S={S1,S2...Sn},通过k个映射函数{f1,f2,......fk},将集合S中的每个元素Sj(1<=j<=n)映射为K个值{g1,g2...gk},然后再将位数组array中相对应的array[g1],array[g2]......array[gk]置为1:

     

    如果要查找某个元素item是否在S中,则通过映射函数{f1,f2,...fk}得到k个值{g1,g2...gk},然后再判断array[g1],array[g2]...array[gk]是否都为1,若全为1,则item在S中,否则item不在S中。

     

    布隆过滤器会造成一定的误判,因为集合中的若干个元素通过映射之后得到的数值恰巧包括g1,g2,...gk,在这种情况下可能会造成误判,但是概率很小。

     

    布隆过滤器实现:

    //ip去重过滤器,布隆过滤器

    public class BloomFilter {

     

        /* BitSet初始分配2^24个bit */

        private static final int DEFAULT_SIZE = 1 << 24;

     

        /* 不同哈希函数的种子,一般应取质数 */

        private static final int[] seeds = new int[] { 5, 7, 11, 13, 31, 37 };

     

        private BitSet bits = new BitSet(DEFAULT_SIZE);

     

        /* 哈希函数对象 */

        private SimpleHash[] func = new SimpleHash[seeds.length];

     

        public BloomFilter() {

           for (int i = 0; i < seeds.length; i++) {

               func[i] = new SimpleHash(DEFAULT_SIZE, seeds[i]);

           }

        }

     

        // 将url标记到bits中

        public void add(String url) {

           for (SimpleHash f : func) {

               bits.set(f.hash(url), true);

           }

        }

     

        // 判断是否已经被bits标记

        public boolean contains(String url) {

           if (StringUtils.isBlank(url)) {

               return false;

           }

     

           boolean ret = true;

           for (SimpleHash f : func) {

               ret = ret && bits.get(f.hash(url));

           }

     

           return ret;

        }

     

        /* 哈希函数类 */

        public static class SimpleHash {

           private int cap;

           private int seed;

     

           public SimpleHash(int cap, int seed) {

               this.cap = cap;

               this.seed = seed;

           }

     

           // hash函数,采用简单的加权和hash

           public int hash(String value) {

               int result = 0;

               int len = value.length();

               for (int i = 0; i < len; i++) {

                  result = seed * result + value.charAt(i);

               }

               return (cap - 1) & result;

           }

        }

    }

     

    7.6.2. redis过滤

    无论我们使用Map、Set、Hash表还是布隆过滤器的方式去重,都是需要占用网络爬虫所在的服务器资源,所以如果碰到超大型的数据处理,还是有所不足

     

    redis也有去重功能,就是set数据类型,我们也可以把需要去重的数据放到redis的set集合中,根据返回结果来判断这条数据是否重复

     

    7.7. URL管理器

    7.7.1. 编写URL管理器

    URL管理器需要有以下5个功能

    1. 添加新的待爬url

    2. 判断待添加url是否存在

    3. 判断是否还有待爬url

    4. 获取待爬url

    5. 修改待爬url为已爬url

     

    public class UrlManager {

     

        private UrlItemMapper urlItemMapper;

     

        // 待爬url

        private List<UrlItem> urlList;

     

        private BloomFilter bloomFilter;

     

        public UrlManager(UrlItemMapper urlItemMapper) {

           this.urlItemMapper = urlItemMapper;

     

           // 初始化布隆过滤器

           this.initBloomFilter();

           // 初始化待查url

           this.queryUrl();

        }

     

        /**

         * 1).添加新的待爬url

         *

         * @param url

         */

        public void addUrlItem(UrlItem urlItem) {

           // 判断商品url是否重复

           if (this.bloomFilter.contains(urlItem.getUrl())) {

               // 如果重复直接返回

               return;

           }

     

           // 如果不重复,保存到数据库中

           this.urlItemMapper.insert(urlItem);

           // 加入到过滤器中

           this.bloomFilter.add(urlItem.getUrl());

        }

     

        /**

         * 2). 判断待添加url是否存在

         *

         * @param url

         * @return

         */

        public boolean contains(String url) {

           return this.bloomFilter.contains(url);

        }

     

        /**

         * 3). 判断是否还有待爬url

         *

         * @return

         */

        public boolean isEmpty() {

           // 判断待爬url集合中是否为空

           if (this.urlList.size() == 0) {

               // 如果为空,则再次从数据库中查询待爬url

               this.queryUrl();

           }

           return this.urlList.size() == 0;

        }

     

        /**

         * 4). 获取待爬url

         *

         * @return

         */

        public UrlItem getUrl() {

           if (this.isEmpty()) {

               // 如果为空,返回null

               return null;

           } else {

               return this.urlList.get(0);

           }

        }

     

        /**

         * 5). 修改待爬url为已爬url

         *

         * @param urlItem

         */

        public void changeUrl(UrlItem urlItem) {

           urlItem.setStatus(false);

           urlItem.setUpdated(new Date());

     

           this.urlItemMapper.updateByPrimaryKeySelective(urlItem);

     

           // 删除待爬url中的元素

           this.urlList.remove(urlItem);

        }

     

        /**

         * 初始化布隆过滤器

         */

        private void initBloomFilter() {

           // 创建布隆过滤器

           this.bloomFilter = new BloomFilter();

     

           // 初始化布隆过滤器

           int page = 1, pageSize = 0;

           do {

               // 设置分页

               PageHelper.startPage(page, 5000);

               // 查询

               List<UrlItem> list = this.urlItemMapper.select(null);

     

               // 查询到的url放到过滤器中

               for (UrlItem urlItem : list) {

                  this.bloomFilter.add(urlItem.getUrl());

               }

     

               page++;

               pageSize = list.size();

     

           } while (pageSize == 5000);

        }

     

        /**

         * 分页查询待查url

         */

        private void queryUrl() {

           // 设置分页

           PageHelper.startPage(1, 500);

           // 设置查询条件

           UrlItem param = new UrlItem();

           param.setStatus(false);

     

           // 查询

           this.urlList = this.urlItemMapper.select(param);

        }

    }

     

    7.7.2. 整合URL管理器

    @Configuration

    public class UrlManagerCfg {

     

        @Bean

        public UrlManager urlManager(UrlItemMapper urlItemMapper) {

           // 创建url管理器

           UrlManager urlManager = new UrlManager(urlItemMapper);

           return urlManager;

        }

    }

     

    8.   爬取商品详情url

     

    8.1. 编写任务

    使用定时任务实现商品url获取,编写定时任务

    @DisallowConcurrentExecution

    public class UrlItemJob extends QuartzJobBean {

     

        private UrlItemCatService urlitemCatService;

        private HttpClientService httpClientService;

        private UrlManager urlManager;

     

        @Override

        protected void executeInternal(JobExecutionContext context) throws JobExecutionException {

           ApplicationContext applicationContext = (ApplicationContext) context.getJobDetail().getJobDataMap()

                  .get("context");

     

           // 获取服务实例

           this.urlitemCatService = applicationContext.getBean(UrlItemCatService.class);

           this.httpClientService = applicationContext.getBean(HttpClientService.class);

           this.urlManager = applicationContext.getBean(UrlManager.class);

     

           // 解析商品类目的页面,获取商品详情url

           List<UrlItemCat> urlItemCats = this.urlitemCatService.queryUrlItemCat();

     

           // 遍历类目页面,获取商品详情url

           for (UrlItemCat urlItemCat : urlItemCats) {

               // 抓取所有数据量较大,这里我们就抓取一页商品,每页是60条数据

               // 根据商品类目,获取该类目的商品详情url

               String next = this.getItemInfo(urlItemCat);

               System.out.println(next);

           }

     

        }

     

        /**

         * 根据商品类目,获取该类目的商品详情url

         *

         * @param url

         * @return 返回下一页url

         */

        private String getItemInfo(UrlItemCat urlItemCat) {

           // 获取html页面

           String html = this.httpClientService.getHtml(urlItemCat.getUrl());

     

           // 使用jsoup解析

           Document doc = Jsoup.parse(html);

           // 获取当前页的所有商品url

           List<String> hrefs = doc.select("div.p-img a").eachAttr("href");

     

           // 保存商品url

           for (String href : hrefs) {

               // 保存

               this.saveUrlItem("https:" + href, urlItemCat.getId());

           }

     

           // 获取当前商品分类的下一页url

           String href = "";

           Elements next = doc.getElementsByClass("pn-next");

           // 如果有下一页,返回下一页url

           if (!next.isEmpty()) {

               href = next.first().attr("href");

           }

     

           return href;

     

        }

     

        /**

         * 保存商品url

         *

         * @param urlItem

         */

        private void saveUrlItem(String href, Long catId) {

           // 判断该url是否存在

           if (this.urlManager.contains(href)) {

               return;

           }

     

           // 不存在则保存

           UrlItem urlItem = new UrlItem();

           urlItem.setCatId(catId);

           urlItem.setUrl(href);

           urlItem.setCreated(new Date());

           urlItem.setUpdated(urlItem.getCreated());

     

           // 保存商品url到数据库

           this.urlManager.addUrlItem(urlItem);

     

        }

     

    }

     

    8.2. 整合任务

    @Configuration

    public class SchedledCfg {

     

        // 定义任务

        @Bean("itemJobBean")

        public JobDetailFactoryBean itemJobBean() {

           JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();

        jobDetailFactoryBean.setApplicationContextJobDataKey("context");

           jobDetailFactoryBean.setJobClass(ItemJob.class);

           jobDetailFactoryBean.setDurability(true);

           return jobDetailFactoryBean;

        }

     

        // 定义触发器

        @Bean("itemJobTrigger")

        public CronTriggerFactoryBean ItemJobTrigger(@Qualifier(value = "itemJobBean") JobDetailFactoryBean itemJobBean) {

           CronTriggerFactoryBean tigger = new CronTriggerFactoryBean();

           tigger.setJobDetail(itemJobBean.getObject());

           tigger.setCronExpression("0/5 * * * * ? ");

           return tigger;

        }

     

        // 定义调度器

        @Bean

        public SchedulerFactoryBean schedulerFactory(CronTrigger[] cronTriggerImpl) {

           SchedulerFactoryBean bean = new SchedulerFactoryBean();

           bean.setTriggers(cronTriggerImpl);

           return bean;

        }

    }

     

    9.   爬取商品详情数据

    9.1. 编写任务

    @DisallowConcurrentExecution

    public class ItemJob extends QuartzJobBean {

     

        private ItemService itemService;

     

        private HttpClientService httpClientService;

        private UrlManager urlManager;

     

        @Override

        protected void executeInternal(JobExecutionContext context) throws JobExecutionException {

           ApplicationContext applicationContext = (ApplicationContext) context.getJobDetail().getJobDataMap()

                  .get("context");

     

           // 获取服务实例

           this.itemService = applicationContext.getBean(ItemService.class);

           this.httpClientService = applicationContext.getBean(HttpClientService.class);

           this.urlManager = applicationContext.getBean(UrlManager.class);

     

           // 获取商品url

           UrlItem urlItem = this.urlManager.getUrl();

     

           // 获取商品数据

           Item item = this.parseItem(urlItem);

     

        }

     

        /**

         * 解析页面获取商品

         *

         * @param html

         * @return

         */

        private Item parseItem(UrlItem urlItem) {

           // 创建商品对象

           Item item = new Item();

     

           // 获取商品页面

           String html = this.httpClientService.getHtml(urlItem.getUrl());

     

           // 使用jsoup解析

           Document doc = Jsoup.parse(html);

     

           // 商品标题

           String title = doc.select("div.sku-name").text();

           item.setTitle(title);

     

           // 商品价格

           // item.setPrice(1l);

     

           // 商品图片

           // item.setImage("");

     

           // 类目id

           item.setCid(urlItem.getCatId());

           // 正常状态

           item.setStatus(1);

           item.setCreated(new Date());

           item.setUpdated(item.getCreated());

           return null;

        }

     

    }

     

    9.2. 整合任务

        // @Bean("urlItemJobBean")

        public JobDetailFactoryBean urlItemJobBean() {

           JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();

        jobDetailFactoryBean.setApplicationContextJobDataKey("context");

           jobDetailFactoryBean.setJobClass(UrlItemJob.class);

           jobDetailFactoryBean.setDurability(true);

           return jobDetailFactoryBean;

        }

     

        // 定义触发器

        // @Bean("urlItemJobTrigger")

        public CronTriggerFactoryBean urlItemJobTrigger(

               @Qualifier(value = "urlItemJobBean") JobDetailFactoryBean urlItemJobBean) {

           CronTriggerFactoryBean tigger = new CronTriggerFactoryBean();

           tigger.setJobDetail(urlItemJobBean.getObject());

           tigger.setCronExpression("0/5 * * * * ? ");

           return tigger;

        }

     

    10.          动态网页

    上面抓取商品详情的例子中,我们发现可以分析静态页面html,但是对js的解析部分还是很薄弱。虽然我们可以读取js的运作机制并且找到相关数据,但是这样会耗费大量时间。

     

    除了人力解析js以外,我们还可以使用工具来模拟浏览器的运行,直接获取解析结果。这里我们使用

    10.1. phantomJs+selenium

         (1)一个基于webkit内核的无头浏览器,即没有UI界面,即它就是一个浏览器,只是其内的点击、翻页等人为相关操作需要程序设计实现。

         (2)提供javascript API接口,即通过编写js程序可以直接与webkit内核交互,在此之上可以结合java语言等,通过java调用js等相关操作,从而解决了以前c/c++才能比较好的基于webkit开发优质采集器的限制。

         (3)提供windows、linux、mac等不同os的安装使用包,也就是说可以在不同平台上二次开发采集项目或是自动项目测试等工作。

     

    下载phantomJs

    在官网上下载

    http://phantomjs.org/download.html

     

    selenium官网

    http://www.seleniumhq.org/projects/webdriver/

     

    工程加入依赖

       <!-- selenium -->

           <dependency>

               <groupId>org.seleniumhq.selenium</groupId>

               <artifactId>selenium-java</artifactId>

               <version>3.4.0</version>

           </dependency>

           <!-- phantomjsdriver -->

           <dependency>

               <groupId>com.codeborne</groupId>

               <artifactId>phantomjsdriver</artifactId>

               <version>1.4.3</version>

           </dependency>

     

     

    11.          其他信息提取

    11.1. 从非html提取文本

    11.1.1. PDF文件

    PDF 是 Adobe公司开发的电子文件格式。这种文件格式与操作系统的平台无关,可以在多数操作系统上通用。

     

            我们经常会遇到这种情况,就是想把PDF文件中的文字复制下来,却发现不能复制,因为PDF文件的内容可能加密了,那么把PDF文件中的内容提取出来就是我们要解决的问题。

     

            现在已经有很多工具可以帮助完成这个任务。例如PDFBox(https://pdfbox.apache.org/)就是专门用来解析PDF文件的Java项目。

     

    11.1.2. Word、Excel文件

    Word是微软公司开发的字处理文件格式,以“doc”或者“docx”作为文件后缀名。Apache的POI(http://poi.apache.org/)可以用来读取Word文档。

    Excel也是微软公司开发的字处理文件格式,是由工作簿(Workbook)组成,工作簿由一个或多个工作表(Sheet)组成,每个工作表都有自己的名称,每个工作表又包含多个单元格(Cell)。除了POI项目,还有开源的jxl可以用来读写Excel。

     

     

     

    11.2. 图像的OCR识别

    11.2.1. OCR介绍

    抓取过程中,如果碰到需要获取的数据是图片的格式,那么我们还需要把图片转换成文字。

     

    从图片中识别出字符叫做光学字符识别(Optical Character Recognition)简称OCR。是指电子设备(例如扫描仪或数码相机)检查纸上打印的字符,通过检测暗、亮的模式确定其形状,然后用字符识别方法将形状翻译成计算机文字的过程。即,针对印刷体字符,采用光学的方式将纸质文档中的文字转换成为黑白点阵的图像文件,并通过识别软件将图像中的文字转换成文本格式。

     

    文字识别包括以下几个步骤:

     (1)图文输入

          是指通过输入图片到计算机中,也就是获取图片。

     

     (2)预处理

           扫描一幅简单的印刷文档的图像,将每一个文字图像分检出来交给识别模块识别,这一过程称为图像预处理。预处理是指在进行文字识别之前的一些准备工作,包括图像净化处理,去掉原始图像中的显见噪声(干扰),

     

     (3)单字识别

          单字识别是体现OCR文字识别的核心技术。从扫描文本中分检出的文字图像,由计算机将其图形,图像转变成文字的标准代码.,是让计算机“认字”的关键,也就是所谓的识别技术。

          识别技术就是特征比较技术,通过和识别特征库的比较,找到特征最相似的字,提取该文字的标准代码,即为识别结果。

     

    (4)后处理

          后处理是指对识别出的文字或多个识别结果采用词组方式进行上下匹配,即将单字识别的结果进行分词,与词库中的词组进行比较,以提高系统的识别率,减少误识率。 汉字字符识别是文字识别领域最为困难的问题,它涉及模式识别,图像处理,数字信号处理,自然语言理解,人工智能,模糊数学,信息论,计算机,中文信息处理等学科,是一门综合性技术。

     

    11.2.2. Tess4j

    Tess4J是一个OCR图片识别技术,我们这里使用的是Tess4J-3.4.2。在windows使用前必须安装Visual C++ 2015 Redistributable Packages,下载地址:

    https://www.microsoft.com/zh-CN/download/details.aspx?id=48145)

     

    首先需要在pom.xml中添加以下依赖

           <dependency>

               <groupId>net.sourceforge.tess4j</groupId>

               <artifactId>tess4j</artifactId>

               <version>3.4.2</version>

         </dependency>

     

    在项目的根路径下复制tessdata文件夹

     

    Tess4j对多种语言都提供相关的语言包,可以在以下地址下载

    https://github.com/tesseract-ocr/tessdata

     

    我们这里实现的是识别图片中的数字。编写测试代码

    public class Tess4jTest {

        public static void main(String[] args) throws Exception {

           CloseableHttpClient httpClient = HttpClients.createDefault();

       

           BufferedImage image = ImageIO.read(new File("C:/Users/tree/Desktop/53281.png"));

           image = image.getSubimage(4, 8, 42, 17);

           Image scaledInstance = image.getScaledInstance(46, 25, image.SCALE_SMOOTH);// 设置缩放目标图片模板

     

           // wr = 46 * 1.0 / bufImg.getWidth(); // 获取缩放比例

           // hr = 25 * 1.0 / bufImg.getHeight();

     

           AffineTransformOp ato = new AffineTransformOp(AffineTransform.getScaleInstance(2.5, 2.5), null);

           scaledInstance = ato.filter(image, null);

     

           File file = new File("tessdata/temp.jpg");

           ImageIO.write((BufferedImage) scaledInstance, "jpg", file);

     

           ITesseract instance = new Tesseract();

           instance.setLanguage("eng");

           long startTime = System.currentTimeMillis();

           // Rectangle rectangle = new Rectangle

           // String ocrResult = instance.doOCR(imgDir, rectangle);

           String ocrResult = instance.doOCR(file);

           // 输出识别结果

           System.out.println("OCR Result: \n" + ocrResult + "\n 耗时:" + (System.currentTimeMillis() - startTime) + "ms");

            System.out.println(NumberUtil.str2NumIp(ocrResult));

        }

     

    }

     

     

     

    11.3. 提取地域信息

    11.3.1. ip地址

    网上有很多的服务商可以提供ip和地址的对应,可以通过这些服务商,根据ip获取到对应的地址。

    有些服务商提供免费的ip库下载,例如纯真ip(http://www.cz88.net/),我们也可以用其作为离线ip库,并加以维护

     

    11.3.2. 电话号码

    我们也可以通过手机电话号码的前七位确定其地址,或者通过其连接互联网使用的ip来进行查询

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • Python爬虫初步个人学习及心得

    万次阅读 多人点赞 2018-02-28 20:11:34
    但是毕设的东西非常的浅显,个人觉得最值得训练的还是Python的爬虫,谨以此开篇,作为学习和练习Python的起步。——————————学习分割线————————————第一次学习的是Python爬取图片信息。网上有很...
  • 学习爬虫总结(一)

    千次阅读 2018-09-28 12:39:15
    爬虫这个名词第一次出现在我的印象中是学习中国大学MOOC里面的python语言程序设计慕课,那时候我对爬虫并不了解,也没有学习的兴趣。最近在看一些有关信息安全的书,发现网上的数据对我们来说很重要,于是对爬虫产生...
  • 现行环境下 大数据与人工智能的重要依托还是庞大的数据和分析采集,类似于... 学习Python基础知识并实现基本的爬虫过程一般获取数据的过程都是按照 发送请求-获得页面反馈-解析并且存储数据 这三个流程来实现的。这...
  • 爬虫学习之路

    2019-08-08 17:33:01
    爬虫学习之路爬虫前奏爬虫前奏1爬虫1爬虫2-正则表达式爬虫3-json文件爬虫4-scrapy框架架构 爬虫前奏 爬虫前奏1 [1] https://download.csdn.net/download/qq_36993547/10952448 包括一些爬虫的代码,慢慢都会上传的 ...
  • java jsoup 网络爬虫 jsoup解析html

    万次阅读 2019-12-16 12:12:38
    java jsoup 网络爬虫 java jsoup 网络爬虫 学习例子(一)抓取豆瓣电影名称+推荐星级 java jsoup 网络爬虫 学习例子(二)只抓取豆瓣电影5星(力荐)电影名称 java jsoup 网络爬虫 学习例子(三)抓取豆瓣电影...
  •  网络爬虫又称网络蜘蛛、网络蚂蚁、网络机器人等,可以自动化浏览网络中的信息,当然浏览信息的时候需要按照我们制定的规则进行,这些规则我们称之为网络爬虫算法。使用Python可以很方便地编写出爬虫程序,进行...
  • 导读 爬虫现在越来越火,随之带来的就是一大波的就业岗位,随之越来越多的人转行学习Python,其中不缺乏Java等语言程序员,难道,爬虫在未来会狠狠的压住其他语言,而一直蝉联冠军吗?什么是爬虫?说起爬虫,很多...
  • Python爬虫(一):编写简单爬虫之新手入门

    万次阅读 多人点赞 2019-07-31 09:34:35
    最近学习了一下python的基础知识,大家一般对“爬虫”这个词,一听就比较熟悉,都知道是爬一些网站上的数据,然后做一些操作整理,得到人们想要的数据,但是怎么写一个爬虫程序代码呢?相信很多人是不会的,今天写一...
  • Python爬虫教程-00-写在前面

    万次阅读 多人点赞 2018-09-15 12:11:13
    鉴于好多人想学Python爬虫,缺没有简单易学的教程,我将在CSDN和大家分享Python爬虫学习笔记,不定期更新 基础要求 Python 基础知识 Python 的基础知识,大家可以去菜鸟教程进行学习 菜鸟教程python基础 ...
  • 无论是从入门级选手到专业级选手都在做的爬虫,还是Web 程序开发、桌面程序开发,又或者是科学计算、图像处理,Python编程都可以胜任。或许是因为这种属性,周围好多小伙伴都开始学习Python。Python爬虫可以自学吗,...
  • python爬虫学习系列

    万次阅读 多人点赞 2018-12-03 09:15:10
    Python爬虫(1):基本原理Python爬虫(2):Requests的基本用法Python爬虫(3):Requests的高级用法Python爬虫(4):Beautiful Soup的常用方法Python爬虫(5):豆瓣读书练手爬虫Python爬虫(6):煎蛋网全站妹子图爬虫Python爬虫(7...
  • Python爬虫:常用的爬虫工具汇总

    千次阅读 多人点赞 2018-10-17 17:06:51
    按照网络爬虫的的思路: 页面下载页面解析数据存储 将工具按照以上分类说明,按照学习路线顺序给出参考文章 页面下载器 requests(必学) python爬虫入门requests模块 Python爬虫:requests库基本使用 Python爬虫:...
  • 想用Python做爬虫,而你却还不会Python的话,那么这些入门基础知识必不可少。...但是却完全不知道从何开始,很迷茫,学的也很杂,下面就跟着小编一起来看看想用python来做爬虫到底需要学习哪些知识吧!
  • python爬虫教程大全

    万次阅读 多人点赞 2013-10-14 12:47:20
    关于python爬虫这方面知识,在网络上有一些教程、文章,很有价值,能够带领新手快速入门。在这里我把自己学习时找到的一些教程列出来,与大家一起分享,欢迎补充! 爬虫 《一只小爬虫》 《一只并发的小爬虫》 ...
  • 零基础如何学爬虫技术?

    千次阅读 2016-07-09 15:40:04
    第一:Python爬虫学习系列教程(来源于某博主:http://cuiqingcai.com/1052.html) Python版本:2.7  整体目录: 一、爬虫入门 1. Python爬虫入门一之综述 2. Python爬虫入门二之爬虫基础了解 ...
  • 导读:网络爬虫也叫做网络机器人,可以代替人们自动地在互联网中进行数据信息的采集与整理。在大数据时代,信息的采集是一项重要的工作,如果单纯靠人力进行信息采集,不仅低效繁琐,...
1 2 3 4 5 ... 20
收藏数 97,359
精华内容 38,943
关键字:

爬虫学习