android爬虫

此文目的,多年后当我回忆起往事,再看此文,或许能重燃我的斗志。

作为android开发,爬虫一直是服务器同事做的。我们都使用java语言,既然他行,那我也可以试试。

当时是这样考虑的:从地址栏中得到网络链接,利用retrofit请求数据,返回数据的格式应当是xml,我再用dom解析xml得到data,然后将data保存到数据库,最后打开数据库的表将数据复制到excel中。程序运行于android 模拟机上,文件保存于sd卡。这样的思路虽然麻烦,但整个看下来还是行得通的。

对于android来说,请求网络如同家常便饭,volley,okhttp,retrofit等这些框架用起来不要太好。数据解析有json,fastjson,gson,更有一些插件,做起来也不费吹灰之力。对于爬虫来说,面对的困难仍有三个,第一个是网页分析,毕竟不是做pc开发的,对js、jqury、ajax等这些前端语言了解较少,只初略的了解css,要想找到特定的url以及相应参数及其调用方式还是存在难度;第二个是网页解析,获取url及相应参数。json解析倒是常用,dom解析虽然之前做过,但之前xml的复杂度远不如当前,想着解析的难度,成功解析所需的时间让我吃不准;第三个是将数据复制到excel表格中,虽然litpal数据库整个操作也不难,我也自信只要获取到数据便能写入数据库,而我不确定的是,litpal数据库的表能直接复制到excel中吗?

虽然有疑问,虽然也预估一些难度,但既然方案是可行的,那便可以尝试尝试了。

首先分析目标页面:
1522318655(1).jpg

我需要获取联系人、联系电话、电子邮箱三项。这些数据都在当前页面中,所以我可以get请求,查看当前页数据。于是使用postman软件,查看返回的数据如下

1522380116(1).jpg

当然这样不便查看,可以查看预览模式


1522379291(1).jpg

惊讶的发现,联系人、电话、电子邮箱这些信息竟然都是空的。这就意味着他是动态加载这些信息的。

使用google浏览器,按F12,可以查看页面代码信息。
1522377182(1).jpg

F5刷新之后,我们看到最后一条耗时较多,是明显的网络请求。
1522379631(1).png

我们可以点开,查看他的请求方式及返回数据。


1522380626(1).png
1522380409(1).jpg

果真如所料,这里面返回的数据是json格式,而内容正是我们爬的信息。我们将他的Request URL和Request heads放于postman测试一下。


image.png

到这一步,可以高兴的说,我们已经掌握单条数据的获取规律,其他数据不过是照葫芦画瓢,抓取到所有数据指日可待。
现在的问题就变成:Request URL如何拼凑。

http://www.kq30.com/wap/index/clx/lu/kq10352619608/cjo/39108/fl/27/?_=1522379555582&cl=0.8682621277798113

想查看如何拼凑,先找到哪里调用。“http://www.kq30.com”这是hostname不用管,我们可以在Element页面,ctrl+f全局搜索一下后面几个路径,比如“/wap/index/clx/lu”,

1522467770(1).jpg

很清晰看到有$.getJson(),可以猜测这里是请求json数据,紧接着是两个变量lu和cjo。而这两个值来源于

var lu=$(".cxd").text(); var cjo=$(".cjo").text();

其中cxd和cjo对应的值正是下方的div节点中。

<div class="cxd">kq6257235281</div>
<div class="cjo">37068</div>

至于后面的“{cl:Math.random}”,猜测就是获取一个随机数,应该没啥影响。

经过以上的分析,我们拼凑request url的问题就变为:解析返回的html,获取节点cxd和cjo对应的值。

至于如何解析返回的html,当然不是dom解析了,因为我在搜索html解析的时候,发现了特别好用的Jsoup解析。Jsoup人家自然鼎鼎大名了,只是我孤陋寡闻今天才第一次结识。

gradle 的dependencies闭包中加入下面这条依赖就行。我使用的是kotlin,故而是“implementation ”而不是“compile”

implementation 'org.jsoup:jsoup:1.11.2'

在使用时,也是极其简单,他内部使用HttpConnection封装了网络请求,以下示例是kotlin语句。

   val newDoc = Jsoup.connect(newUrl).get()
   val lu = newDoc.select("div.cxd").html()
   val cjo = newDoc.select("div.cjo").html()

既然已经拿到lu和cjo这两个关键的参数,表明详情页我们已经破解,那接下来的对手就是列表页了。我们要从列表页找出每一行对应的连接,分析其是否存在规律。

1522469746(1).jpg

按下f12,点击左上角箭头,将鼠标移动到列表的一个条目上点击,会弹出对应的代码
1522470543(1).jpg

可以看出,在class="kqb"节点下,有许多的<li class="...">这样的节点,在其子节点下<a href="...">的值就是我们想要的url.

可以看出这些url并无规律,所以我们使用一个list集合来保存这些url.
我们继续使用Jsoup解析列表网页。

 var doc = Jsoup.connect("http://www.kq30.com/zp/ci/kz/37/di/2/p/" + i + "/").data("Referer", "http://www.kq30.com/").get()
 val element = doc.select("div.kqb")
 val link = element.select("a").eachAttr("href")

上述代码中,url中的i代表第几页,后面的.data(key,value)是request header的参数。我也是无意中发现eachAttr("")返回的竟然是一个list集合,真是大快人心啊。这样我就获取到了每一页中的每一条的url.

当然这些url存在重复和非我们所需的,所以有必要做一些筛选。当然这些属于优化,后面再提。现在我们把前面的思路顺着理一遍,再推出接下来要做的事。

首选通过Jsoup解析列表页面,得到当前页所有的详情页连接,再通过Jsoup解析详情页连接,得到lu和cjo的值,最后通过retrofit请求经过lu和cjo拼凑的url,得到目标json数据。好了,有了json数据,接下来的问题就是保存数据到excel了。

在搜索android 保存数据到excel中,我看到这篇文章“https://blog.csdn.net/linzhenxiang123/article/details/53730439”,知道了有两种方式,一种是jxl,一种是poi.我顺便看了这两种方式的优缺点:

1)、如果你用的是jxl.jar包的话模板的Excel表格必须是97-03版的Excel,否则的话 jxl.jar 是没法进行解析的

2)、如果你用的是poi.jar包的话模板的Excel表格是没有要求

可见poi使用的限制性条件较少,而且我安装的是16版的excel,所以果断使用poi了。
下载poi的jar包,"http://mirrors.hust.edu.cn/apache/poi/release/bin/poi-bin-3.17-20170915.tar.gz"
解压后,将jar包一起复制到android studio 工程项目的libs目录下,并右键add as library。
poi配置完毕,那就是简单的使用了

    val wb = HSSFWorkbook()
    val sheet = wb.createSheet("口腔信息")
    val header = sheet.createRow(0)
          header.createCell(0).setCellValue("称呼")
          header.createCell(1).setCellValue("电话")
          header.createCell(2).setCellValue("qq")
          header.createCell(3).setCellValue("座机号")
//设置行宽,20代表字符
        for (i in 0..header.physicalNumberOfCells) {
            sheet.setColumnWidth(i, 255 * 20)
        }
//保存到手机外部存储
 val path = Environment.getExternalStorageDirectory().absolutePath.toString().trim() + "/info.xlsx"
            val os = FileOutputStream(path)
            wb.write(os)
            wb.close()
            os.close()

由于poi是先保存数据,最后统一写入excel文件中,所以我们每次获取到的json数据就 sheet.createRow(i)。先创建第ii行,接着依次header.createCell(i).setCellValue(value),给第i行第i个单元格赋值。

到现在我们克服重重障碍,从解析网页到把数据写入excel,整个流程算是走通了。虽说解决了大方向的问题,然而仍存在一些小问题。

1.效率低下,经测试发现,爬完第一页数据需要30秒,爬完第二页数据需要1分钟,下一页时间总是上一页加30秒,以此类推时间慢的可怕。很明显,代码中出现了数据重复累加,于是检查代码,每次进入新的一页,都将数据清空。

2.数据重复,数据重复的原因在两方面,一方面是相同的url多次请求,另一方面是不同的url返回相同的数据信息。所以分别对url去重和对phoneNumber去重。因为手机号能够唯一标识一位用户。

3.耗时操作,不断请求网络是一个严重的耗时操作,将数据写入excel也是耗时操作。采用异步那是自然。起初只是在activcity里面执行异步,但activity生命周期经常变动,且爬虫所需时间略长,异步任务运行于后台当内存紧张时,有可能被杀死,所以有必要使用service。

4.锁屏断网,我使用的是vivo真机,当锁屏一分钟左右,应用的网络便会断开,同时会报出SocketTimeoutException。针对这个问题,我并没有找到有效的解决办法。只是让手机屏幕常亮来避免这个问题,当然这样的做法很耗电。

5.kotlin无法使用java1.8,在使用poi保存数据到excel中时,2016版的excel,需要使用XSSFWorkbook()的API,XSSFWorkbook()的API是运行在java1.8的环境中,而kotlin使用的java是1.6,所以很尴尬的在kotlin中无法使用。虽然网上有方法说配置kotlin支持1.8

    kotlinOptions {
        jvmTarget = '1.8'
    }

但我试了之后,并没效果,所以最终只能采用HSSFWorkbook()的API。

爬虫虽然有各种脚本框架,其效率与扩展性不知甩我多远,但自己经过不断尝试,努力实现自己的构思,这种快感又岂是直接使用框架所能带来的。值此一役,除了让我熟悉了网页分析、Jsoup与poi的简单使用,更令我振奋的是:大胆尝试,车到山前必有路。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容

  • 最近做了个获取Kindle特价书的app(https://www.coolapk.com/apk/167660),...
    ifadai阅读 2,971评论 0 15
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,669评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,183评论 25 707
  • HTML、XML、XHTML 有什么区别? HTML: HTML是超文本标记语言 (Hyper Text Mark...
    饥人谷_LEO阅读 338评论 0 0
  • 昨天刚从网上得知英国物理学家史蒂芬·威廉·霍金去世的消息,我就无比心疼王俊凯,因为我知道小凯又要被无脑喷子黑得体无...
    渡梦人间1005DAYTOY阅读 979评论 3 5