• 热门专题

开发一个像btbook.net这样的搜片神器

作者:卖知了的老太太  发布日期:2014-03-06 21:47:25
  • 这几天btbook.net这个搜片神器网站火了, 让我这个无工作的人, 也想做一个出来, 不然时间不好打发, 本人的草稿站: fastbot.me (刚发布几个小时, 体验等几天再做)

    现在说说这种搜片神器是怎么做的:

    一, 先实现一个DHT爬虫
    至于怎么实现, 我就不多说了, 我这里有几篇文章说了. 

    1, 大白话讲解DHT原理及出现历史

    2, 如何'养'一只DHT爬虫

    3, 如何让DHT爬虫走得更远

    4, 我与DHT爬虫那些操蛋事儿

    花点时间去看看吧, 里面有贴有我的两个DHT爬虫的github地址


    DHT爬虫, 相当于汽车里的发动机, 技术最难实现, 但也是最重要的, 虽然可以使用别人DHT爬虫, 但是要整合到自己搜片神器的话, 还是需要一定的技术的, 自己写的话, 成就感更大.


    二, 种子下载
    种子下载也是很麻烦的事情, 如果是自己实现协议进行种子下载的话, 下载种子时特别费时间, 没有好的网络带宽, 就别想了.
    直接说下如何到迅雷服务器去下载种子文件, 还有如何对torrage.com定向采集来先充个面子.

    用WireShark抓包分析迅雷, 发现它是这么个流程: 得到一个磁力链接, 取出那40字节的infohash, 在自己的种子服务器进行HTTP请求, 看自己是否已缓存过, 如果已缓存, 直接下载. 如果没有缓存, 就走DHT网去下载了. 这就是为什么你看到迅雷有时候下载种子快和慢的现象, 快的时候是因为直接到自己种子服务器下载, 慢的是因为要经过一定数量的向DHT网络发送get_peers请求.

    ok, 只要我们实现了DHT爬虫, 得到infohash后, 就可以到迅雷种子库去下载. 那么向迅雷种子库进行HTTP请求时, 是个什么构造呢? 抓包分析图我就不截图了, 直接文字描述下.

    原始infohash: ad2150e029b61caee9edc5f4e9b02cee489788cd
    大写infohash: AD2150E029B61CAEE9EDC5F4E9B02CEE489788CD
    请求地址: http://bt.box.n0808.com/AD/CD/AD2150E029B61CAEE9EDC5F4E9B02CEE489788CD.torrent
    header头信息: Referer: http://bt.box.n0808.com (必须写, 不然返回error信息)

    就这么简单, 一定要注意AD是infohash头两个字符, CD是infohash最后两个字符.

    如果迅雷种子服务器也没有你给定的infohash的种子的话, 那就算了, 除非你自己去实现种子下载协议

    补充: 听我的一些'追随者'说, 不知道如何通过infohash下载种子, 在这里就贴一些通过迅雷下载种子, 通过torrage.com下载的代码例子:

    #encoding: utf-8
    from time import time
    
    def torrentInfo(torrentContent):
        metadata = torrentContent["info"]
        info = {
            "name": getName(metadata),
            "length": calcLength(metadata),
            "timestamp": getCreateDate(torrentContent),
            "files": extraFiles(metadata)
        }
        return info
    
    def calcLength(metadata):
        length = 0
        try:
            length = metadata["length"]
        except KeyError:
            try:
                for file in metadata["files"]:
                    length += file["length"]
            except KeyError:
                pass
        return length
    
    def extraFiles(metadata):
        files = []
        try:
            for file in metadata["files"]:
                path = file["path"]
                if len(path) > 1:
                    main = path[0]
                    for f in path[1:]:
                        files.append("%s/%s" % (main, f))
                else:
                    files.append( path[0] )
            if files:
                return '\r\n'.join(files)
            else:
                return getName(metadata)
        except KeyError:
            return getName(metadata)
    
    def getName(metadata):
        try:
            name = metadata["name"]
            if name.strip() == "": raise KeyError
        except KeyError:
            name = getMaxFile(metadata)
    
        return name
    
    def getMaxFile(metadata):
        try:
            maxFile = metadata["files"][0]
            for file in metadata["files"]:
                if file["length"] > maxFile["length"]:
                    maxFile = file
            name = maxFile["path"][0]
            return name
        except KeyError:
            return ""
    
    def getCreateDate(torrentContent):
        try:
            timestamp = torrentContent["creation date"]
        except KeyError:
            timestamp = int( time() )
        return timestamp
    
    解析种子文件的基本函数(torrent.py)
    解析种子文件的基本函数(torrent.py) 
    #encoding: utf-8
    import socket
    from time import time
    
    import requests
    from bencode import bdecode, BTL
    
    from torrent import *
    
    class Thunder(object):
        def __init__(self, store):
            self.cache = {}
            self.store = store
    
        def download(self, infohash):
            if self.store.exists(infohash):
                self.store.incrHot(infohash)
            else:
                try:
                    self.cache[infohash] += 1
                except KeyError:
                    self.cache[infohash] = 1
                    tc = self._download(infohash)
                    try:
                        tc = bdecode(tc)
                        info = torrentInfo(tc)
                        hot = self.cache[infohash]
                        self.store.save(infohash, info["name"], info["length"], info["timestamp"], hot, info["files"])
                    except Exception:
                        pass
                    finally:
                        del self.cache[infohash]
    
        def _download(self, infohash):
            infohash = infohash.upper()
            start = infohash[0:2]
            end = infohash[-2:]
    
            url = "http://bt.box.n0808.com/%s/%s/%s.torrent" % (start, end, infohash)
            headers = {
                "Referer": "http://bt.box.n0808.com"
            }
            try:
                r = requests.get(url, headers=headers, timeout=10)
                if r.status_code == 200:
                    return r.content
            except (socket.timeout, requests.exceptions.Timeout), e:
                pass
            return ""
    
    用infohash组成磁力链接到迅雷种子库下载种子(thunder.py)
    用infohash组成磁力链接到迅雷种子库下载种子(thunder.py) 
    #encoding: utf-8
    import re
    from time import sleep
    
    import requests
    import gevent
    from gevent import monkey
    monkey.patch_all()
    from bencode import bdecode
    
    from torrent import *
    
    JOBS = 50
    TIMEOUT = 10
    
    def start(infohashfileStore, resourceStore):
        downloadHashFile(infohashfileStore, resourceStore)
    
    def downloadPage():
        try:
            r = requests.get("http://torrage.com/sync", timeout=TIMEOUT*60)
            return r.content
        except Exception:
            return ""
    
    def downloadHashFile(infohashfileStore, resourceStore):
        c = downloadPage()
        files = extractInfohashFiles(c)
        for file in files:
            temp = []
            if infohashfileStore.exists(file): continue
            r = requests.get("http://torrage.com/sync/%s" % (file,))
            for infohash in r.iter_lines():
                if resourceStore.exists(infohash):
                    resourceStore.incrHot(infohash)
                else:
                    if len(temp) == JOBS:
                        downTorrentInGevent(temp, resourceStore)
                        temp = []
                    else:
                        temp.append(infohash)
                    #downTorrent(infohash, resourceStore)
            downTorrentInGevent(temp, resourceStore)
            infohashfileStore.save(file)
    
    def downTorrentInGevent(infohashes, resourceStore):
          jobs = [ gevent.spawn(downTorrent, infohash, resourceStore) for infohash in infohashes]
          gevent.joinall(jobs)
    
    def downTorrent(infohash, resourceStore):
        try:
            r = requests.get("http://torrage.com/torrent/%s.torrent" % (infohash, ), timeout=TIMEOUT)
            if r.status_code==200:
                info = torrentInfo(bdecode(r.content))
                resourceStore.save(infohash=infohash, name=info["name"], 
                    length=info["length"], timestamp=info["timestamp"],
                    files=info["files"], hot=1)
        except Exception:
            pass
    
    def extractInfohashFiles(content):
        c = re.compile(r'')
        return c.findall(content)
    
    用infohash组成磁力链接到torrage.com下载种子文件(torrage.py)
    用infohash组成磁力链接到torrage.com下载种子文件(torrage.py)

    那个torrage.com又是干啥的? 假如你也要做一个搜片神器的网站的话, 那么你最好先迅速积累资源, 后面再慢慢用DHT爬虫收集infohash向迅雷请求种子下载或自己实现种子下载协议进行下载. 打开torrage.com/sync, 里面都是存有infohash的文本文件, 种子下载就写如: http://torrage.com/torrent/640FE84C613C17F663551D218689A64E8AEBEABE.torrent 就可以了. 注意的是, 这个文本文件里的infohash有重复的, 要注意先去重复.

    顺便说句, 不知torrage.com是不是被墙了, 时不时地要抽风, 要定向采集它的, 服务器最好放在国外.

    三, 种子文件分析
    读取文件内容, 使用bencode模块'解码'为编程语言的数据结构, 详情请自行google. 种子文件里有name, files, create date, length等属性值.


    四, 搜索引擎
    把torrage.com的种子全部下载完, 后期又要用自己的DHT爬虫爬的话, 那么数据可是上5000万了, 没有好的中文分词搜索引擎, 那是绝对不可能的, 所以要用Sphinx或Lucene类似的. 笔者目前用的是Sphinx, 不过把玩得不好, 只会完全索引, 实时增量索引还在研究中. 这个就不好写了.

    fastbot.me所采用技术工具:

    simDHT + Python + Flask + jinjia2 + MySQL + Sphinx + Gentoo Linux + requests + Gevent + Twisted

     

延伸阅读:

About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规