【问题标题】:Django, scraping: What's the best way to detect "changes" while scraping?Django,抓取:在抓取时检测“变化”的最佳方法是什么?
【发布时间】:2018-12-25 00:25:12
【问题描述】:

这不是典型的代码问题,而是我现在面临的设计问题。


假设我有一个网页(不是我的),我想抓取一些信息。对我来说,最重要的信息是when(日期时间)角色登录他注销的时间,但我收集的其他信息是好吧。 Login 从点 2 已知(见下文),但 logout 我必须计算 我可以访问 2 个页面:

  1. http://x/online.php - 它为我提供了在线昵称列表(200 - 500 个条目)
  2. http://x/character.php?name=昵称 - 它为我提供了每个昵称的详细信息,例如:角色名称、公会、性别、等级、等级(职业)、状态(离线/在线)、上次登录。

我在 tasks.py 中只做了 2 个“操作”,它们是:

  • A - update_or_create 使用来自“2”点的信息
  • B - 获取在线列表 - 使用“1”点的信息

所以,它现在的工作方式是(每分钟,感谢 Celery,我都这样做):

  • 将事务自动提交设置为 False
  • 为具有 login 注册表但没有 logout 的角色执行 A
  • 如果此时角色的状态为“OFFLINE”,则将数据库条目添加到 logout 作为 datetime.now
  • 对列表 B 中的字符执行 A
  • 提交并将事务自动提交设置为 True

问题是,我不确定这是否是个好主意。 我的 models.py:(评论只是为了澄清我在做什么)

class Guild:
     name = models.CharField(max_length=100)

class Player(models.Model):
    #FK:
    guild =  models.CharField(max_length=50, null=True, blank=True) # Does he have guild?
    name = models.CharField(max_length=100, unique=True) 
    sex = models.CharField(choices=SEX_CHOICES, max_length=7) # Male / Female
    level = models.PositiveSmallIntegerField() 
    vocation = models.CharField(choices=VOCATION_CHOICES, max_length=50) # His class
    status = models.CharField(choices=ONLINE_CHOICES, max_length=10) # Offline / Online
    lastlogin = models.DateTimeField() 

    def __str__(self):
        return self.name


class Deaths(models.Model):
    text = models.CharField(max_length=500)
    killed = models.ForeignKey(Player, null=True, on_delete=models.CASCADE, related_name='killed') # Who got killed
    killer = models.ForeignKey(Player, null=True, on_delete=models.CASCADE, related_name='killer') # Who killed him

    date = models.DateTimeField() # When he died?
    level = models.PositiveSmallIntegerField() # On which level player died
    pvp = models.BooleanField() # Death was due to PvP or PvE?

    class Meta:
        ordering = ('date',)


class OnlineDetails(models.Model):
    player = models.ForeignKey(Player, on_delete=models.CASCADE)

    login = models.DateTimeField() # When he logged in
    logout = models.DateTimeField(null=True, blank=True) # When he logged off

    def __str__(self):
        return self.player.name + " " + str(self.logout) if self.logout else self.player.name

    class Meta:
        ordering = ('logout', 'login')

它有效,但我想知道这是否是最佳方法。实际上,我认为这种方式很糟糕,因为我必须在一分钟内扫描超过 500 个字符,这使得使用“antyddos”盾牌很难。

您有什么更好的解决方案或技术值得我学习吗? 我在 python 和 django 方面都不是最好的,还在学习中。

【问题讨论】:

  • 您是否检查了网站是否提供了 API 端点?如果您每分钟检查 500 个条目,他们会在某个时候阻止您...
  • 他们确实不提供 API。
  • 因为你的页面没有简单和高效的方法来做到这一点......所以你将不得不检查页面响应(如果 url 机会)和一些标志性标签和类,如果你爬虫在该结构中找不到东西,它可能已更改。
  • 越具体越容易发现机会,但很难检查整个页面以了解真正发生的变化
  • 也许...只是也许...您可以获取整个 HTML 页面并进行比较...但看起来不太好

标签: python django web-scraping


【解决方案1】:

当然,您可以测量整个过程,需要多长时间等等,但我认为更新约 500 个条目需要几毫秒。更大的问题可能是每分钟抓取 500 个条目,这意味着您必须每秒向它们发送大约 8 个请求(基于第 2 点而不是第 1 点)。我认为你正在刮掉第 1 点。每一分钟,你都会刮掉丢失的字符。第 1 点根本不是问题。解析这么多页面可能很困难,但并非不可能。另外,我建议您下载页面并将它们存储一段时间,如果在此过程中出现任何故障,下载页面并在其他线程中并行解析它们会更快,因为最困难的是发送请求和下载响应.对于事务自动提交......这可能是多线程环境中的问题。如果值得冒险不知道发生了什么,您应该尝试使用和不使用它来衡量过程。

【讨论】:

    猜你喜欢
    • 2019-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-08
    • 1970-01-01
    • 2011-11-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多