qq_74845768 2024-06-18 11:15 采纳率: 42.9%
浏览 16

Python爬取番茄小说网连接数据库存不了数据,json文件也是空的

目标是爬取番茄小说网的作小说名称、作者、路径、状态、简介、发布时间,连接数据库是成功的,但是却不能把数据存入数据库,保存的json格式的文件也是空的,只有方括号,整个程序并没有报错,代码如下:

from lxml import etree
import requests
import json
import threading
from queue import Queue
import time
import pymysql

class Tomato:
    def __init__(self):
        self.headers = {
            "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.81 Safari/537.36"
        }
        self.url_queue = Queue()
        self.html_queue = Queue()
        self.content_queue = Queue()

    def get_url_queue(self):
        url_temp = "https://fanqienovel.com/library/all/page_{}?sort=hottes"
        url_list = [url_temp.format(i) for i in range(1, 9)]
        for url in url_list:
            self.url_queue.put(url)

    def get_html_queue(self):
        while True:
            url = self.url_queue.get()
            html_source_page = requests.get(url, headers=self.headers).text
            self.html_queue.put(html_source_page)
            self.url_queue.task_done()

    def parse_html(self):
        while True:
            content_list = []
            html = self.html_queue.get()
            html_str = etree.HTML(html)
            node_list = html_str.xpath("//div[@class='book-item-text']")

            title_num = 0
            for node in node_list:
                title = node.xpath('./div/a/text()')[0]
                url = node.xpath('./div/a/@href')[0]
                author = node.xpath('./div[@class="book-item-desc"][1]/span/text()')[0]
                status = node.xpath('./div[@class="book-item-desc"][2]/span/text()')[0]
                intro = node.xpath('./div[contains(@class,"book-item-abstract")]/text()')[0]
                time = node.xpath("./div[@class='book-item-footer']/span/text()")[0].replace('@', '').strip()

                # 构建JSON格式的字符串
                item = {
                    "小说名称": title,
                    "小说链接": url,
                    "小说作者": author,
                    "状态":status,
                    "小说简介":intro,
                    '发布时间': time,
                }
                content_list.append(item)
                title_num += 1
            self.content_queue.put(content_list)
            self.saveData(content_list)
            self.html_queue.task_done()

    def save(self):
        while True:
            content_list = self.content_queue.get()
            with open("tomato.json", mode="a+", encoding='utf-8') as f:
                f.write(json.dumps(content_list, ensure_ascii=False, indent=2))
            self.content_queue.task_done()

    def saveData(self, content_list):  # 添加 self 参数
        db = pymysql.connect(host='localhost', user='root', password='root', database='py_tomato')
        cursor = db.cursor()
        for i in range(len(content_list)):
            item = content_list[i]
            article = item.get("小说名称")
            url = item.get("小说链接")
            author = item.get("小说作者")
            status = item.get("状态")
            intro = item.get("小说简介")
            publish_time = item.get("发布时间")
            sql = "insert into heima (article,url,author,status,intro,time) values(%s,%s,%s,%s,%s,%s)"
            try:
                cursor.execute(sql, (article, url, author,status,intro, publish_time))  # 传入正确的参数
                db.commit()
            except Exception as e:
                print("插入数据失败", e)
                db.rollback()
        cursor.close()
        db.close()

    def run(self):
        thread_list = []
        t_url = threading.Thread(target=self.get_url_queue)
        thread_list.append(t_url)
        for page in range(3):
            t_content = threading.Thread(target=self.get_html_queue)
            thread_list.append(t_content)
        for j in range(3):
            t_content = threading.Thread(target=self.parse_html)
            thread_list.append(t_content)
        t_save = threading.Thread(target=self.save)
        thread_list.append(t_save)
        for t in thread_list:
            t.setDaemon(True)
            t.start()
        for q in [self.url_queue, self.html_queue, self.content_queue]:
            q.join()
        print("打印完成")


if __name__ == '__main__':
    tomato = Tomato()
    tomato.run()


  • 写回答

2条回答 默认 最新

  • 专家-赤兔[在线] 优质创作者: 编程框架技术领域 2024-06-18 11:16
    关注

    引自免费微信小程序:皆我百晓生

    从你提供的代码来看,主要的问题在于saveData方法中对数据库操作的部分,以及save方法中写入JSON文件的逻辑可能没有正确处理多线程环境下的并发写入问题。下面我会逐步分析并提供解决方案。

    数据库操作问题

    saveData方法中,你尝试将数据插入到数据库,但可能存在一些小错误导致插入失败,具体如下:

    1. SQL语句中的字段名与表结构不匹配:确保heima表中的字段名与你在SQL语句中使用的字段名一致。例如,如果表中字段名为article_name而非article,则应修改SQL语句中的字段名。

    2. 参数类型不匹配:在执行cursor.execute()时,确保传递给SQL语句的参数类型与字段类型相匹配。例如,如果time字段在数据库中被定义为日期时间类型,而你传入的是字符串,这可能导致插入失败。

    3. 异常处理:虽然你已经包含了异常处理,但确保异常信息能够提供足够的细节来定位问题。

    JSON文件写入问题

    save方法中,你尝试将内容列表写入JSON文件。然而,在多线程环境中,多个线程可能同时尝试写入同一个文件,这会导致数据混乱。解决这个问题的方法是在写入文件前添加互斥锁(mutex)以确保同一时刻只有一个线程可以写入文件。

    解决方案

    1. 检查数据库字段名

    确保数据库表的字段名与你代码中使用的字段名完全一致。如果字段名不同,更新你的SQL语句。

    2. 使用互斥锁控制文件写入

    为了防止多线程同时写入文件,可以使用threading.Lock来控制文件访问。以下是修改后的save方法示例:

    import threading
    
    # 创建一个锁对象
    file_lock = threading.Lock()
    
    def save(self):
        while True:
            content_list = self.content_queue.get()
            # 使用锁保护文件写入操作
            with file_lock:
                with open("tomato.json", mode="a+", encoding='utf-8') as f:
                    f.write(json.dumps(content_list, ensure_ascii=False, indent=2))
            self.content_queue.task_done()
    

    3. 更新saveData方法中的SQL语句

    确保SQL语句中的字段名与数据库表的实际字段名匹配,并且参数类型正确。如果有必要,调整字段名和参数类型。

    4. 检查异常信息

    在异常处理部分,打印出更详细的错误信息,以便于调试。例如:

    except Exception as e:
        print(f"插入数据失败: {e}")
        db.rollback()
    

    通过以上步骤,你应该能够解决数据插入数据库和写入JSON文件的问题。如果还有其他具体错误信息,建议进一步检查错误日志或输出,以便更准确地定位问题所在。

    评论 编辑记录

报告相同问题?

问题事件

  • 创建了问题 6月18日