【问题标题】:Can't get a script to remember its last scraped results无法让脚本记住其上次抓取的结果
【发布时间】:2019-09-07 20:00:07
【问题描述】:

我在 python 中创建了一个脚本来从网站上获取不同帖子的标题,它可以完美地抓取它们。

但是,我现在希望这个脚本做的是记住最后一次抓取的结果,这样当我运行它两次时,它就不会获取相同的结果。更清楚一点 - 脚本将在第一次执行时像往常一样解析结果,但在没有找到新帖子之前,它不会在后续执行中获取相同的结果。

使用 csv

import csv
import requests
from bs4 import BeautifulSoup

def get_posts(url):   
    response = requests.get(url)
    soup = BeautifulSoup(response.text,"lxml")
    for item in soup.select(".summary .question-hyperlink"):
        yield item.text

if __name__ == '__main__':
    link = 'https://stackoverflow.com/questions/tagged/web-scraping'
    with open("output.csv","w",newline="") as f:
        writer = csv.writer(f)
        for item in get_posts(link):
            writer.writerow([item])
            print(item)

使用数据库

import mysql.connector
from bs4 import BeautifulSoup
import requests

url = "https://stackoverflow.com/questions/tagged/web-scraping"

def connect():
    mydb = mysql.connector.connect(
      host="localhost",
      user="root",
      passwd = "",
      database="mydatabase"
    )
    return mydb

def create_table(link):
    conn = connect()
    mycursor = conn.cursor()
    mycursor.execute("DROP TABLE if exists webdata")
    mycursor.execute("CREATE TABLE if not exists webdata (name VARCHAR(255))")

    response = requests.get(link)
    soup = BeautifulSoup(response.text,"lxml")
    for items in soup.select(".summary"):
        name = items.select_one(".question-hyperlink").get_text(strip=True)
        mycursor.execute("INSERT INTO webdata (name) VALUES (%s)",(name,))
    conn.commit()

def fetch_data():
    conn = connect()
    mycursor = conn.cursor()
    mycursor.execute("SELECT * FROM webdata")
    for item in mycursor.fetchall():
        print(item)

if __name__ == '__main__':
    create_table(url)
    fetch_data()

上述脚本每次运行时都会解析相同的结果。

如何让我的脚本记住上次抓取的结果,以便在后续执行中不会再次抓取相同的结果?

【问题讨论】:

  • 将结果存储在 db 或 .txt 文件中?
  • 记住最后的抓取 URL 怎么样?如果脚本采用此 Url - 中断。
  • 无论如何您都必须进行刮擦和比较,那么为什么不简单地重新覆盖/写入呢? SO 结果会转移页面,因此无论如何您都必须检查每个链接。
  • 当答案简单时,不费吹灰之力的问题仍然可能得到有用的答案。您要问的问题很复杂,而且您的问题给人的印象(可能是错误的)是您没有做出足够的努力来理解没有简单的方法可以实现您想要实现的目标。这不仅仅是有点棘手

标签: python python-3.x web-scraping beautifulsoup


【解决方案1】:

您需要为每个帖子创建一个唯一 ids 列表,StackOverflow 已经使用 <div class="question-summary" id="some unique id"> 做到了这一点。您可以使用以下方法提取此值:

def get_posts(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.text,"lxml")
    for item in soup.select(".question-summary"):
        yield item['id'], item.findChild('a', {'class':'question-hyperlink'}).text

这会返回唯一的 ID,以及每个问题的标题。

现在您需要将此唯一 id 与已添加到 csv 文件中的 id 进行比较,如果 id 已存在,则跳过该行。这是此工作的代码:

if __name__ == '__main__':
    link = 'https://stackoverflow.com/questions/tagged/web-scraping'
    file = open('./output.csv', 'r')
    reader = csv.reader(file)
    ids = [row[0] for row in reader] #this extracts first column of each row into a list
    file.close()

    with open('./output.csv', 'w', newline="") as f:
        writer = csv.writer(f)
        for id, title in get_posts(link):

            if id not in ids: # if id isn't already in your list of ids, write the row

                writer.writerow([id, title])

值得注意的是,这不是最佳解决方案。最好使用 sqlite 或 mysql 之类的数据库,并为每个帖子的 id 列添加唯一索引。这样一来,数据库会自动处理重复的帖子,您不必为每次抓取都将整个 csv 文件拉入内存(两次)。

使用 MySQL 的示例

表定义:

sql = '''
    CREATE TABLE `webdata` (
    id INT AUTO_INCREMENT PRIMARY KEY,
    question_id CHAR(30) NOT NULL,
    question_title CHAR(75) NOT NULL,
    UNIQUE KEY(question_id)
)
'''

mycursor.execute(sql)

批量插入抓取数据:

def get_posts(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.text,"lxml")
    results = []
    for item in soup.select(".question-summary"):
        question_id = item['id']
        question_title = item.findChild('a', {'class':'question-hyperlink'}).text
        results.append((question_id, question_title))

    return results

sql = 'INSERT IGNORE INTO `webdata` (question_id, question_title) VALUES (%s, %s)'

mycursor.executemany(sql, get_posts(url))

【讨论】:

    【解决方案2】:

    您需要将抓取运行的结果写入某个持久性存储——例如,数据库或某个文件,正如评论中指出的那样。然后在您下次运行时,首先读取该文件,以便您的程序知道上一次运行中发生了什么。

    所有细节,例如决定如何和在哪里存储结果,以何种格式存储结果,以及在读回结果后如何使用结果,都留给读者作为练习。

    如果您知道如何将抓取结果映射到关系数据库架构,我建议使用 Python 的内置 sqlite 来存储它们。

    再想一想,也许您可​​以将内存中的数据腌制或云端腌制到一个文件中,然后在下次运行时取消腌制。

    【讨论】:

      猜你喜欢
      • 2020-08-26
      • 1970-01-01
      • 2016-12-22
      • 2013-12-17
      • 2019-07-31
      • 1970-01-01
      • 2023-02-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多