【问题标题】:Docker POSTGRESQL - Insert multiple rows with string and bytea dataDocker POSTGRESQL - 使用字符串和字节数据插入多行
【发布时间】:2021-05-29 02:21:37
【问题描述】:

我在使用psycopg2 在 PostgreSQL 数据库中插入二进制值 (bytea) 时遇到了一些困难。我是 Postgres 的新手,请原谅我的无知。

所以,下面是场景:

  1. 我正在抓取我的大学网站(使用 BeautifulSoup)并检索他们在通知部分发布的通知。
  2. 这是一个糟糕的网站,所以通知只发布在一个网页上,通知文本超链接到一个 pdf 文档。
  3. 我抓取了 7 个子页面。我知道我没有使用正确的 web-dev 术语,所以我的意思是:根 URL 是 https://exampleuniversity.com/,子页面是(粗体)'https://exampleuniversity.com/univ-通告/'、'https://exampleuniversity.com/univ-notification/' 等 - 其中有 7 个
  4. 这 7 个子页面被放置在一个列表中(称为paths),我遍历它们中的每一个(并将其附加到根 URL)并抓取通知文本、URL 和 pdf 文件本身。这一切都像一个魅力。问题是当我需要将它们写入(docker)PostgreSQL 表时。

仅供参考 - docker PostgreSQL 容器部分本身工作正常。

您可以想象,当我遍历 子页面 的列表时,我会得到一批(抓取的)文本 + URL + pdf 文件,我需要将它们写入数据库。

https://www.postgresqltutorial.com/postgresql-python/blob/之后,我了解到我必须open(file, 'rb').read(),并在插入时执行psycopg2.Binary(<variable_name>)

好的。但这必须在每次迭代中完成。所以有一个列列表(例如,texts 是一列,urls 是一列等)。所以跟随psycopg2: insert multiple rows with one queryant32 回答),我了解到我必须先args_str = ','.join(cur.mogrify("(%s,%s,%s,%s,%s,%s,%s,%s,%s)", x) for x in tup) 然后cur.execute("INSERT INTO table VALUES " + args_str) - 请假设我正确放置了变量和占位符。

所以,我压缩检索到的文本 + URLs + open('file', 'rb').read()(这是每次迭代)并制作一个元组并尝试遵循上面的示例,并且(自然)它失败了(仅供参考:元组中的每个项目都是现在由三个元素组成的组,形成需要插入数据库的 row - 与前面讨论的列相反)。这就是我遇到的问题。

抛出的错误是:sequence item 0: expected str instance, bytes found

在下面的代码中,一些 cmets 被标记为“TO StackOverflow”。这会向你解释我想做什么。

代码如下:

for path in paths:
    for i in range(retry_attempts):
        try:
            #Creating directoty to save the pdf files
            dir_to_create = f'./app/{today}/{path}'
            if not os.path.exists(dir_to_create):
                os.makedirs(dir_to_create)

            print(f'\n****** Scraping - {path} ******'.upper())
            univ_notification_url = univ_url + path
            univ_notification_page = requests.get(univ_notification_url)
            soup = BeautifulSoup(univ_notification_page.content, 'html.parser')
            items = soup.find_all('div', {'class': 'entry'})
            desired_p_tags = items[0].find_all('p')

            #Removing <p> tags with no href (only with the string values as mentioned below)
            for _ in desired_p_tags:
                if _.text == 'University Circulars' or 'Circulars, Notifications, Letters' in _.text:
                    desired_p_tags.remove(_)

            #Only retaining <p> tags that point to a pdf files (so the ones linking to another webpage is omitted)
            desired_p_tags = [_ for _ in desired_p_tags if '.pdf'  in (_.find('a'))['href']]

            texts = [_.text for _ in desired_p_tags]
            urls = [(_.find('a'))['href'] for _ in desired_p_tags]

            url_hash = [] #TO STACKOVERFLOW: I am also creating a blake2b hash for the files that I download and save it in the db. So the zipped tuple will contain this too
            pdf_obj = []
            for url in urls:
                filename = os.path.basename(url) # To get the name of the downloaded file 
                urllib.request.urlretrieve(url, f'{dir_to_create}/{filename}') # Downloading the file
                pdf_obj.append(open(f'{dir_to_create}/{filename}', 'rb').read()) # Appending into the list the binary form of the downloaded file
                url_hash.append(hashlib.blake2b(open(f'{dir_to_create}/{filename}', 'rb').read()).hexdigest()) # Hashing the file
                print(f"Downloaded {filename}\n")
                
            data_to_insert = tuple(zip(texts, urls, pdf_obj, url_hash)) #Zipping the variables into a tuple

            # DATABASE PART
            conn = psycopg2.connect(
                host = 'pg_db',
                port = '5432',
                database = 'bu_notifications', 
                user = 'superuser', 
                password = 'User1234!'
            )

            cur = conn.cursor()

            args_str = ','.join(cur.mogrify("(%s,%s,%s,%s)", x) for x in data_to_insert)
            cur.execute("INSERT INTO table00 (notification_text, item_url, pdf_file, pdf_hash) VALUES (%s, %s, %s, %s)" + args_str)
            conn.commit()
            //Close connection and cursor (also not writing down the except clause that follows after a try)

【问题讨论】:

    标签: python postgresql insert psycopg2 bytea


    【解决方案1】:

    好的。我设法修复它。我就是这样做的:

    1. 我创建了一个包含 psycopg2.Binary(open(f'{dir_to_create}/{filename}', 'rb').read()) 本身的值的列表,以及已经存在的 URL 和文本列表。然后从三个中创建元组。

    2. 然后遍历元组并像这样将每个元素插入到表中(请注意 - 还有一列):

               for _ in data_to_insert:
               cur.execute("INSERT INTO table00 (notification_text, item_url, pdf_file, pdf_hash) VALUES (%s, %s, %s, %s)", _)
               conn.commit()
      

    它现在写入数据库(至少我在 PGAdmin 中看到它)。现在我需要找到一种从本地下载它的方法,但这是另一个 Stack Overflow 帖子。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-19
      • 2014-12-11
      • 2015-08-16
      相关资源
      最近更新 更多