在 Python 中使用 BS4、Selenium 抓取动态数据并避免重复

Posted

技术标签:

【中文标题】在 Python 中使用 BS4、Selenium 抓取动态数据并避免重复【英文标题】:Scraping Dynamic Data and Avoiding Duplicates with BS4, Selenium in Python 【发布时间】:2017-06-11 03:28:03 【问题描述】:

我在这里尝试做的是从不断重新加载信息的动态页面中检索数据。我设置它的方式是每 60 秒刷新一次。问题是,旧数据不会从页面中删除,因此当程序在刷新后遍历数据时,会出现重复。

注意:程序在开始时休眠,因为最初没有要抓取的消息。

我正在寻找一种方法或解决方案,以使用最后一条记录(在本例中为消息 [-1])作为搜索的起点,以防止重复。

感谢所有帮助!谢谢。

driver.get(URL)
while 1==1:
    time.sleep(60)
    chat_page = driver.page_source
    chat_soup = BeautifulSoup(chat_page,'lxml')
    messages = chat_soup.findAll('div', attrs='class':'message first')
    for message in messages:
        username = message.div.h2.span.strong.text
        text = message.find('div', attrs='class':'markup').get_text()
        timestamp = message.find('span', attrs='class':'timestamp').get_text()
        today = str(datetime.date.today())
        timestamp = timestamp.replace('Today', today)

        usernames.append(username)
        timestamps.append(timestamp)
        texts.append(text)
        print(timestamp, username," : ",text)

我创建了一个临时解决方案,可以在将每条记录输入我的 SQLite3 数据库之前对其进行检查。该程序可以通过使用“插入或忽略”来工作。不幸的是,该程序一直在检查重复项,因为它无法过滤掉已经被抓取的数据。以下是我的临时解决方案:

driver.get(URL)
while 1==1:
    chat_page = driver.page_source
    chat_soup = BeautifulSoup(chat_page,'lxml')
    messages = chat_soup.findAll('div', attrs='class':'message first')
    for message in reversed(messages):
        username = message.div.h2.span.strong.text
        usernames.append(username)
        text = message.find('div', attrs='class':'markup').get_text()
        text = text.replace('"', '')
        text = text.replace("'", "")
        username = username.replace('"', '')
        username = username.replace("'", "")
        timestamp = message.find('span', attrs='class':'timestamp').get_text()
        today = str(datetime.date.today())
        timestamp = timestamp.replace('Today', today)
        isbot = message.find('span', attrs='class':'bot-tag')
        if (isbot):
            username = '(BOT) ' + username
        sql = '''INSERT OR IGNORE INTO 'chats' ('timestamp', 'username', 'text') VALUES ("%s", "%s", "%s")''' % (timestamp, username, text)
        conn.executescript(sql)

【问题讨论】:

【参考方案1】:

我找到了一个使用 set.difference 的解决方案,效果很好。

在我的问题中,一次存在一定数量的数据(比如说 10 个)。我们希望获得没有旧值的新值。

 olddata = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 newdata = [5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
 unique_data = set(newdata).difference(olddata)

=

 11, 12, 13, 14

最终工作代码:

while 1==1:
    chat_page = driver.page_source
    chat_soup = BeautifulSoup(chat_page,'lxml')
    messages = chat_soup.findAll('div', attrs='class':'message first')
    messages_dedupe = set(messages).difference(oldlist)
    for message in messages_dedupe:
        username = message.div.h2.span.strong.text
        text = message.find('div', attrs='class':'markup').get_text()
        timestamp = message.find('span', attrs='class':'timestamp').get_text()
        today = str(datetime.date.today())
        timestamp = timestamp.replace('Today', today)
        isbot = message.find('span', attrs='class':'bot-tag')
        if (isbot):
            username = '(BOT) ' + username
        usernames.append(username)
        timestamps.append(timestamp)
        texts.append(text)
        oldlist = messages
        sqlvalues = (username, timestamp, text)
        c.execute("INSERT OR IGNORE INTO db (username, timestamp, text) VALUES (?, ?, ?)", sqlvalues)
        conn.commit()
        print(timestamp, username,":",text)
    time.sleep(20)

【讨论】:

【参考方案2】:

所以您正在寻找一种方法来避免检查每条记录是否有重复项?假设每个时间戳都是一个唯一值,并且 reversed(messages) 按从最新消息到最旧消息的顺序排列。

timestamp_array = []
while 1==1:
    chat_page = driver.page_source
    chat_soup = BeautifulSoup(chat_page,'lxml')
    messages = chat_soup.findAll('div', attrs='class':'message first') 
    for message in reversed(messages):
        username = message.div.h2.span.strong.text
        usernames.append(username)
        text = message.find('div', attrs='class':'markup').get_text()
        text = text.replace('"', '')
        text = text.replace("'", "")
        username = username.replace('"', '')
        username = username.replace("'", "")
        timestamp = message.find('span', attrs='class':'timestamp').get_text()
        today = str(datetime.date.today())
        timestamp = timestamp.replace('Today', today)
        isbot = message.find('span', attrs='class':'bot-tag')
        if (isbot):
            username = '(BOT) ' + username
        if timestamp in timestamp_array:
            break
        timestamp_array.append(timestamp)
        sql = '''INSERT OR IGNORE INTO 'chats' ('timestamp', 'username', 'text') VALUES ("%s", "%s", "%s")''' % (timestamp, username, text)
        conn.executescript(sql)

一旦到达第一个副本,这将跳出 for 循环。

【讨论】:

您是否建议我只需将 reversed() 添加到我的 for 语句中?我不完全理解您的建议,我已经查看了相关文档。 我试过这个解决方案,它只是颠倒了顺序,但没有帮助,对不起。 听起来您希望从messages 的最后一个元素开始迭代?您能否澄清一下“我正在寻找一种方法或解决方案来使用最后一条记录(在这种情况下是消息[-1])作为搜索的起点” 我改写了标题,对此我深表歉意。 我最想做的就是找到一个能够正确利用我所拥有信息的解决方案。我知道有一种方法可以对此进行编码,而无需检查每条记录上的重复项。我进一步更新了线程。非常感谢您提供帮助。

以上是关于在 Python 中使用 BS4、Selenium 抓取动态数据并避免重复的主要内容,如果未能解决你的问题,请参考以下文章

selenium+python自动化89-用例不通过的时候发送邮件

使用 selenium 和 bs4 进行网页抓取

使用 BS4 或 Selenium 从 finishline.com 抓取网页

使用 selenium 和 bs4 抓取网站不起作用

将链接从 selenium 迭代到 bs4 并打印剥离的字符串

“AttributeError:‘str’对象没有属性‘descendants’错误,使用 bs4 和 selenium 进行自动抓取