0

我正在使用Telethon库来抓取一些电报频道。在爬行时,我需要解析许多加入链接、用户名和频道 ID。为了解决这些问题,我使用了方法client.get_entity(),但过了一段时间,电报服务器禁止我的爬虫解析太多用户名。我四处搜索并从这个问题中发现,我应该使用get_input_entity()而不是get_entity()。实际上,telethon 将实体保存在本地 SQLite 文件中,每当调用时get_input_entity(),它首先搜索本地 SQLite 数据库,如果没有找到匹配项,则向电报服务器发送请求。到目前为止一切顺利,但我对这种方法有两个问题:

  1. get_input_entity()只返回两个属性:ID哈希,但 SQLite 数据库中还有其他列,例如usernamephonename。我需要一种方法,不仅要返回IDhash,还要返回其他列。
  2. 我需要控制发送到电报服务器的解析请求的数量,但get_input_entity()只要在本地数据库中找不到匹配项,就会向电报服务器发送请求。问题是我无法控制这种方法何时请求电报服务器。实际上,我需要此方法的布尔参数,指示当在本地数据库中找不到匹配项时,该方法是否应向电报服务器发送请求。

我阅读了一些 Telethon 源代码,主要是get_input_entity()编写了我自己的版本get_input_entity()

def my_own_get_input_entity(self, target, with_info: bool = False):
    if self._client:
        if target in ('me', 'self'):
            return types.InputPeerSelf()
        def get_info():
            nonlocal self, result
            res_id = 0
            if isinstance(result, InputPeerChannel):
                res_id = result.channel_id
            elif isinstance(result, InputPeerChat):
                res_id = result.chat_id
            elif isinstance(result, InputPeerUser):
                res_id = result.user_id
            return self._sqlite_session._execute(
                'select username, name from entities where id = ?', res_id
            )
        try:
            result = self._client.session.get_input_entity(target)
            info = get_info() if with_info else None
            return result, info
        except ValueError:
            record_current_time()

        try:
            # when we are here, we are actually going to
            # send request to telegram servers
            if not check_if_appropriate_time_elapsed_from_last_telegram_request():
                return None
            result = self._client.get_input_entity(target)
            info = get_info() if with_info else None
            return result, info
        except ChannelPrivateError:
            pass
        except ValueError:
            pass
        except Exception:
            pass

但是我的代码在某种程度上存在性能问题,因为它对 SQLite 数据库进行了冗余查询。例如,如果target实际是本地数据库中的实体并且with_infoTrue,它首先查询本地数据库self._client.session.get_input_entity(target)并检查是否with_infoTrue,然后再次查询数据库以获取用户名名称列。在另一种情况下,如果target在本地数据库中找不到,则self._client.get_input_entity(target)调用会对本地数据库进行冗余调用。

了解了这些性能问题后,我深入研究了 telethon 源代码,但由于我对asyncio了解不多,所以我写不出比上面更好的代码了。

任何想法如何解决这些问题?

4

1 回答 1

1

client.session.get_input_entity将不进行 API 调用(它不能),如果本地数据库中没有匹配项,则失败,这可能是您想要的行为。

您现在可以访问client.session._conn私有属性。它是一个sqlite3.Connection对象,因此您可以使用它来进行您想要的所有查询。请注意,由于您正在访问私有成员,因此这很容易中断,尽管预计很快不会发生任何变化。理想情况下,您应该对会话文件进行子类化以满足您的需要。请参阅文档中的会话文件

于 2018-09-14T09:15:51.777 回答