V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
imlonghao
V2EX  ›  程序员

使用 Pyspider 爬取京东 Wap 版本商品价格

  •  
  •   imlonghao ·
    imlonghao · 2014-12-31 20:12:57 +08:00 · 9781 次点击
    这是一个创建于 3611 天前的主题,其中的信息可能已经有所发展或是发生改变。

    爬虫运行在 @binux 的 Pyspider


    总想找一个站来练习一下写爬虫,于是乎,我找到了京东的Wap版。

    关于京东Wap版

    优点
    1. 没有反爬虫的设置,似乎不限制并发链接
    2. 单个页面大小比较小,对VPS来说节省流量
    3. 链接结构比较整齐,比较容易分析

    缺点
    1. 可以采集的数据比较少,只有商品分类、名称和价格
    2. 单个分类分页最多2000页,但其实不仅仅有2000页

    链接分析

    我们大概的思路就是:全部分类-->二级分类-->三级分类-->遍历全部分页-->采集数据

    打开wap.jd.com后,我们不难就可以看出全部分类的地址:http://wap.jd.com/category/all.html

    然后我们观察链接,二级分类的地址均是以http://wap.jd.com/category/开头的

    三级分类的地址均是以http://wap.jd.com/products/开头的

    然后,我们就可以看到商品列表页了。

    在商品页中,有两类链接我们需要分析。一是商品详细页的地址,以http://wap.jd.com/product/开头;另一个是页面的页数,我们使用Pyspider的分析工具,可以知道页面的页数是HTML>BODY>DIV.page>A[href]里面的。

    知道了上面的信息,我们基本上就可以写代码开始采集了。

    注意

    京东的地址中,会传入一个区别不同用户的cid和sid的值,例如我的就是cid=1323&sid=24faaa1458222af7f13as9kf3aa12337,实际上链接只有.html前面的部分是有用的,.html后面?开始其实都是可以忽略的。

    在Pyspider中,系统是通过url来区别不同的地址的,如果是sid不同的话,会被识别成不同的页面,最后的话可能会造成重复采集的结果。

    因此,我打算使用urlparse模块中的urljoin来处理这些地址,可能方法有点不对,但是还是达到了效

    代码

    请参考我的网站:https://imlonghao.com/Pyspider/wap.jd.com.html

    后记

    使用Pyspider的效率我个人还是满意的,总共大概就是我2天爬了将近500W商品,速度其实可以再进一步,因为我不敢开太大并发因为已经VPS的内存不够..............

    除此之外,硬盘也不够了,记录使用默认的配置,results.db总共占用了2.1G,而tasks.db占用了12G左右

    此外,给@binux 反馈一个问题,像我这样500W数据的话,通过/results/dump/jd.json无法导出数据,显示超时....

    第 1 条附言  ·  2015-01-01 01:21:32 +08:00
    无法导出数据的问题已经解决,系前端与webui单线程等多个原因导致的,目前已经解决。
    需要这500w商品数据进行大数据分析得可以联系我
    35 条回复    2016-02-16 12:30:57 +08:00
    binux
        1
    binux  
       2014-12-31 20:24:23 +08:00   ❤️ 1
    pyspider 前面还有反代吗? pyspider 是流式输出的,虽然打开表很慢,但是应该还是能输出的
    imlonghao
        2
    imlonghao  
    OP
       2014-12-31 20:29:56 +08:00
    @binux 可能是这个的问题吧,前面有nginx和varnish,我试一试直接下载:5000的看看
    imlonghao
        3
    imlonghao  
    OP
       2014-12-31 20:37:02 +08:00
    root@pyspider:~# wget 127.0.0.1:5000/results/dump/jd.json
    --2014-12-31 20:31:43-- http://127.0.0.1:5000/results/dump/jd.json
    Connecting to 127.0.0.1:5000... connected.
    HTTP request sent, awaiting response...

    就这样就不动了,我记得如果是流式输出的话wget不是这样的..
    wangfeng3769
        4
    wangfeng3769  
       2014-12-31 20:40:29 +08:00   ❤️ 1
    想知道你是怎么爬取北京之外的商品情况的 比如天津的情况。
    imlonghao
        5
    imlonghao  
    OP
       2014-12-31 20:44:14 +08:00
    @wangfeng3769 我只是爬了商品的名称、分类以及价格,没有爬有没有货这个..
    wangfeng3769
        6
    wangfeng3769  
       2014-12-31 20:49:28 +08:00
    @imlonghao 这个接口默认是北京的 ,想知道你是怎么做到爬取天津情况的
    imlonghao
        7
    imlonghao  
    OP
       2014-12-31 20:57:11 +08:00
    @wangfeng3769
    我刚刚说了我并没有爬不同地区的商品情况,我的VPS是日本的,所以只能爬了北京的情况。

    不过我还是根据你的需求看了看京东的设计,大概能满足你的有求了。

    商品页:
    天津 > 东丽区 > 全境
    provinceId=3 天津
    cityId=51035 东丽区 | cityId=51042 静海区 | 等等...
    countryId=39620 全境

    其中,countryId默认天津都是全境,不需要另外设置,只需要设置cityID和provinceId即可。

    想要看那个地区的库存情况,爬虫的时候设置不同天津(provinceId=3)地区的cityID即可。

    我所贴的代码:
    self.crawl(urljoin(each.attr.href,'?=').replace('?=',''), callback=self.in_page)

    你要看天津的,就可以改成:
    self.crawl(urljoin(each.attr.href,'?province=3&cityID=51042'), callback=self.in_page)

    等等,其他自己发挥
    binux
        8
    binux  
       2014-12-31 21:04:01 +08:00   ❤️ 1
    @imlonghao 嗯,那确实是个问题。应该是 tornado + Flask 就没法用流了
    imlonghao
        9
    imlonghao  
    OP
       2014-12-31 21:07:09 +08:00
    @binux
    您看看吧。那这样的话要导出数据只能通过db那里来导?打算换去mysql好导出一下..
    另外,我爬京东的时候用35/30这样来爬,算是快么?
    binux
        10
    binux  
       2014-12-31 21:09:34 +08:00
    @imlonghao 非常快
    invite
        11
    invite  
       2014-12-31 23:32:51 +08:00
    有限制的, 前段时间就爬过.
    imlonghao
        12
    imlonghao  
    OP
       2014-12-31 23:35:32 +08:00 via Android
    @invite 那可能我人品好?35页/秒跑了500w商品没封
    invite
        13
    invite  
       2014-12-31 23:43:17 +08:00
    @imlonghao 你可以先看看, 爬出来的结果, 有没有不是你想要的.
    imlonghao
        14
    imlonghao  
    OP
       2014-12-31 23:46:31 +08:00 via Android
    @invite 检查过的了,都是想要的结果,如果返回403之类的不会记录的,现在只是苦于不能优雅地导出数据。
    binux
        15
    binux  
       2015-01-01 00:04:32 +08:00
    @imlonghao 我改过了,试试看
    imlonghao
        16
    imlonghao  
    OP
       2015-01-01 00:15:25 +08:00 via Android
    @binux 看到了,等我把之前的数据导入mysql再试试,待会@ 你
    新年快乐
    lifsth
        17
    lifsth  
       2015-01-01 20:26:50 +08:00
    楼主 服务器 108.61.250.165是否已经失效?
    imlonghao
        18
    imlonghao  
    OP
       2015-01-01 20:53:07 +08:00
    @lifsth 超流量了,关掉了
    benjiam
        19
    benjiam  
       2015-01-02 02:09:00 +08:00
    这个貌似很爽 我3年前玩的时候,512M linode 20分钟抓取50w 产品。那时候京东总共也就50万种产品,价格还是图片要识别。现在有500万种了?
    imlonghao
        20
    imlonghao  
    OP
       2015-01-02 07:36:28 +08:00 via Android
    @benjiam 20分钟50w也是超快的了,我几天才爬了500w,而且还没爬完,你要看数据的话可以发给你
    marksaas
        21
    marksaas  
       2015-01-02 11:12:26 +08:00
    发我一份吧,mark_wk#qq.com,多谢。。
    benjiam
        22
    benjiam  
       2015-01-02 17:35:56 +08:00
    你是拉所有的产品详细情况还是 产品列表吗?

    http://wap.jd.com/products/1315-1343-1354-0-0-0-0-0-0-0-1-1-2.html?cid=1354&sid=e3fd661f50a653203402decbfe71b57e

    这种产品列表吗?如果是产品列表的话,应该非常快

    我简单的计算了一下
    500万产品, 单页大概150K, 每页大概15种产品 gzip 的话 算压缩到0.25

    500.0* 10 * 1024 * 150 * 1024 / 15 / 4 /1024/ 1024/1024
    大概是12G 数据
    imlonghao
        23
    imlonghao  
    OP
       2015-01-02 17:46:57 +08:00 via Android
    @benjiam 只保存了商品名字,价格和分类。最后json格式,只有1.9G,不知道我有没有数错行数,你要的话可以留邮箱拿去看看
    benjiam
        24
    benjiam  
       2015-06-07 23:50:29 +08:00
    貌似已经不能用来 现在默认会跳到m.jd.com
    benjiam
        25
    benjiam  
       2015-06-07 23:56:43 +08:00
    已经破解 很简单
    UnderIndex
        26
    UnderIndex  
       2015-07-27 18:24:30 +08:00
    @benjiam 你遇到过评论重复的情况吗,就是请求太频繁会返回的评论结果是一样的。
    l0wkey
        27
    l0wkey  
       2016-02-15 12:04:21 +08:00
    @binux
    我也遇到无法导出了, wget 的 json 流,数据量是 300w 多一点

    --2016-02-15 11:51:41-- http://127.0.0.1:5000/results/dump/zhi.json
    Connecting to 127.0.0.1:5000... connected.
    HTTP request sent, awaiting response... 401 UNAUTHORIZED
    Connecting to 127.0.0.1:5000... connected.
    HTTP request sent, awaiting response... 500 INTERNAL SERVER ERROR
    2016-02-15 11:52:41 ERROR 500: INTERNAL SERVER ERROR.
    binux
        28
    binux  
       2016-02-15 23:27:18 +08:00 via Android
    @l0wkey 日志,栈信息
    l0wkey
        29
    l0wkey  
       2016-02-15 23:58:55 +08:00
    @binux
    日志无相关信息..
    binux
        30
    binux  
       2016-02-16 02:12:07 +08:00
    @l0wkey webui 出错时的日志
    l0wkey
        31
    l0wkey  
       2016-02-16 11:16:37 +08:00
    @binux
    [E 160215 11:35:00 app:1423] Exception on /results/dump/zhihu.json [GET]
    Traceback (most recent call last):
    File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
    File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
    File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
    File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
    File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
    File "/usr/local/lib/python2.7/dist-packages/pyspider/webui/result.py", line 37, in dump_result
    resultdb.get(project, 'any')
    File "/usr/local/lib/python2.7/dist-packages/pyspider/database/mysql/resultdb.py", line 99, in get
    where=where, where_values=(taskid, )):
    File "/usr/local/lib/python2.7/dist-packages/pyspider/database/basedb.py", line 69, in _select2dic
    dbcur = self._execute(sql_query, where_values)
    File "/usr/local/lib/python2.7/dist-packages/pyspider/database/basedb.py", line 36, in _execute
    dbcur.execute(sql_query, values)
    File "/usr/lib/python2.7/dist-packages/mysql/connector/cursor.py", line 515, in execute
    self._handle_result(self._connection.cmd_query(stmt))
    File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
    File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
    File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
    File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
    File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
    File "/usr/local/lib/python2.7/dist-packages/pyspider/webui/result.py", line 37, in dump_result
    resultdb.get(project, 'any')
    File "/usr/local/lib/python2.7/dist-packages/pyspider/database/mysql/resultdb.py", line 99, in get
    where=where, where_values=(taskid, )):
    File "/usr/local/lib/python2.7/dist-packages/pyspider/database/basedb.py", line 69, in _select2dic
    dbcur = self._execute(sql_query, where_values)
    File "/usr/local/lib/python2.7/dist-packages/pyspider/database/basedb.py", line 36, in _execute
    dbcur.execute(sql_query, values)
    File "/usr/lib/python2.7/dist-packages/mysql/connector/cursor.py", line 515, in execute
    self._handle_result(self._connection.cmd_query(stmt))
    File "/usr/lib/python2.7/dist-packages/mysql/connector/connection.py", line 488, in cmd_query
    result = self._handle_result(self._send_cmd(ServerCmd.QUERY, query))
    File "/usr/lib/python2.7/dist-packages/mysql/connector/connection.py", line 395, in _handle_result
    raise errors.get_exception(packet)
    DatabaseError: 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
    [I 160215 11:35:00 _internal:87] 127.0.0.1 - - [15/Feb/2016 11:35:00] "GET /results/dump/zhihu.json HTTP/1.1" 500 -
    binux
        32
    binux  
       2016-02-16 11:36:58 +08:00
    @l0wkey 你访问 http://localhost:5000/task/project:any 也 500 吗?
    l0wkey
        33
    l0wkey  
       2016-02-16 11:45:36 +08:00
    @binux
    /task/project:any 404
    /task/project:_NAME 也 404

    指的 /tasks?project=_NAME 么?这个访问正常
    binux
        34
    binux  
       2016-02-16 12:18:36 +08:00
    @l0wkey project 替换成你导出的 project
    l0wkey
        35
    l0wkey  
       2016-02-16 12:30:57 +08:00
    @binux
    /task/_NAME:any 404
    /task/_NAME 400
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3574 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 04:25 · PVG 12:25 · LAX 20:25 · JFK 23:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.