1. Python爬虫数据存储入门PyMySQL与MongoDB实战最近在帮几个刚入门的同学解决Python爬虫数据存储的问题发现很多新手在连接MySQL和MongoDB时都会遇到各种坑。今天我就以最直白的方式手把手带你搞定PyMySQL的安装使用和常见import问题的解决方案。先说说为什么需要数据库存储。当你爬取的数据量超过几百条时txt或csv文件就会变得难以管理。我去年做的一个电商价格监控项目每天要存储近10万条数据关系型数据库MySQL适合存储结构化数据比如商品名称、价格、销量等表格型数据而MongoDB这类非关系型数据库则擅长处理半结构化数据如包含嵌套结构的JSON数据。两者在爬虫项目中各有所长建议都掌握。2. PyMySQL安装与配置详解2.1 环境准备在开始之前请确保已安装Python 3.6推荐3.8有可用的MySQL服务本地或远程知道数据库的连接信息主机、端口、用户名、密码注意如果还没安装MySQLWindows用户可以使用XAMPP等集成环境Mac用户推荐用Homebrew安装Linux用户直接用apt-get或yum安装即可。2.2 PyMySQL安装实战打开你的命令行CMD/Terminal执行以下命令pip install pymysql如果遇到安装缓慢的问题可以换用国内镜像源pip install pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后可以通过以下命令验证是否成功python -c import pymysql; print(pymysql.__version__)如果输出版本号如1.0.2说明安装成功。2.3 解决import报错问题新手最常遇到的几个问题ModuleNotFoundError: No module named pymysql可能原因虚拟环境未激活/安装到了其他Python版本解决方案# 确认当前Python环境 which python # 确认pip所属Python版本 pip -V # 如果不一致使用完整路径安装 /usr/local/bin/python3.8 -m pip install pymysqlImportError: cannot import name connect通常是因为文件命名冲突比如你的脚本就叫pymysql.py解决方案重命名你的脚本文件避免与库同名SSL连接问题如果出现SSL相关错误可以禁用SSLconn pymysql.connect(..., ssl{disabled: True})3. PyMySQL基础操作全解析3.1 建立数据库连接import pymysql # 基础连接参数 conn pymysql.connect( hostlocalhost, # 数据库服务器地址 port3306, # 端口默认3306 userroot, # 用户名 password123456, # 密码 databasetest, # 数据库名 charsetutf8mb4, # 字符编码 cursorclasspymysql.cursors.DictCursor # 返回字典格式数据 )重要提示生产环境千万不要把密码硬编码在代码里推荐使用环境变量或配置文件import os password os.getenv(MYSQL_PASSWORD)3.2 基本CRUD操作创建表with conn.cursor() as cursor: sql CREATE TABLE IF NOT EXISTS articles ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) NOT NULL, content TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) cursor.execute(sql) conn.commit()插入数据防SQL注入的正确姿势data { title: Python爬虫实战, content: 这是一篇关于PyMySQL使用的教程 } with conn.cursor() as cursor: sql INSERT INTO articles (title, content) VALUES (%s, %s) cursor.execute(sql, (data[title], data[content])) conn.commit()批量插入爬虫数据存储必备articles [ {title: 文章1, content: 内容1}, {title: 文章2, content: 内容2} ] with conn.cursor() as cursor: sql INSERT INTO articles (title, content) VALUES (%s, %s) cursor.executemany(sql, [(a[title], a[content]) for a in articles]) conn.commit()查询数据with conn.cursor() as cursor: cursor.execute(SELECT * FROM articles WHERE title LIKE %s, (%Python%,)) results cursor.fetchall() for row in results: print(fID: {row[id]}, 标题: {row[title]})4. 爬虫项目集成实战4.1 新闻爬虫MySQL存储案例以爬取简书首页文章为例import requests import pymysql from bs4 import BeautifulSoup # 数据库配置 db_config { host: localhost, user: root, password: 123456, db: jianshu, charset: utf8mb4 } def create_table(): conn pymysql.connect(**db_config) with conn.cursor() as cursor: sql CREATE TABLE IF NOT EXISTS jianshu_articles ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) NOT NULL, author VARCHAR(100), publish_time VARCHAR(50), content TEXT, url VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) cursor.execute(sql) conn.commit() conn.close() def scrape_and_store(): url https://www.jianshu.com/ headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } response requests.get(url, headersheaders) soup BeautifulSoup(response.text, html.parser) articles soup.find_all(article, class_have-img) conn pymysql.connect(**db_config) with conn.cursor() as cursor: for article in articles: title article.find(a, class_title).get_text(stripTrue) author article.find(a, class_nickname).get_text(stripTrue) publish_time article.find(span, class_time)[data-shared-at] url https://www.jianshu.com article.find(a, class_title)[href] # 获取文章详情 detail_resp requests.get(url, headersheaders) detail_soup BeautifulSoup(detail_resp.text, html.parser) content detail_soup.find(article).get_text(stripTrue)[:2000] # 截取前2000字 sql INSERT INTO jianshu_articles (title, author, publish_time, content, url) VALUES (%s, %s, %s, %s, %s) cursor.execute(sql, (title, author, publish_time, content, url)) conn.commit() conn.close() if __name__ __main__: create_table() scrape_and_store()4.2 性能优化技巧连接池管理频繁创建连接会影响性能推荐使用DBUtils连接池from dbutils.pooled_db import PooledDB pool PooledDB( creatorpymysql, maxconnections5, **db_config ) def query_data(): conn pool.connection() with conn.cursor() as cursor: cursor.execute(SELECT * FROM articles) return cursor.fetchall() conn.close()批量提交爬虫场景下建议每100-1000条数据提交一次而不是每条都commitBATCH_SIZE 500 count 0 with conn.cursor() as cursor: for item in data: cursor.execute(insert_sql, (item[field1], item[field2])) count 1 if count % BATCH_SIZE 0: conn.commit() conn.commit() # 提交剩余数据异常处理网络爬虫必须健壮try: with conn.cursor() as cursor: cursor.execute(sql, params) conn.commit() except pymysql.Error as e: print(f数据库错误: {e}) conn.rollback() except requests.exceptions.RequestException as e: print(f网络请求失败: {e}) finally: conn.close()5. MongoDB快速上手5.1 PyMongo安装与基础使用安装pip install pymongo基本操作from pymongo import MongoClient # 连接MongoDB client MongoClient(mongodb://localhost:27017/) # 选择/创建数据库 db client[crawler_db] # 选择/创建集合类似MySQL的表 collection db[articles] # 插入数据 article { title: MongoDB教程, author: 张三, tags: [数据库, NoSQL] } collection.insert_one(article) # 查询 for doc in collection.find({tags: NoSQL}): print(doc)5.2 与MySQL的对比选择特性MySQLMongoDB数据结构结构化表格式半结构化JSON文档适合场景关系明确、需要复杂查询数据结构多变、快速迭代爬虫应用典型用例电商产品信息、用户数据社交媒体数据、日志信息写入性能中等需要事务支持高无事务开销复杂查询能力强SQL丰富中等聚合管道6. 常见问题解决方案6.1 连接问题排查Cant connect to MySQL server检查MySQL服务是否运行sudo service mysql status检查防火墙设置sudo ufw allow 3306确认连接参数是否正确特别是host/portAccess denied for user确认用户名密码正确检查用户权限GRANT ALL PRIVILEGES ON database.* TO userhostLost connection to MySQL server通常是因为连接超时可以设置连接参数conn pymysql.connect( ..., connect_timeout10, read_timeout30, write_timeout30 )6.2 编码问题如果遇到中文乱码确保数据库和表使用utf8mb4编码连接时指定charsetconn pymysql.connect(..., charsetutf8mb4)对于MongoDBPython 3.x默认使用utf-8一般无需特别处理6.3 性能优化索引优化对经常查询的字段创建索引# MySQL with conn.cursor() as cursor: cursor.execute(CREATE INDEX idx_title ON articles(title)) # MongoDB collection.create_index([(title, pymongo.ASCENDING)])批量操作MySQL批量插入cursor.executemany(sql, data_list)MongoDB批量插入collection.insert_many(documents)异步操作对于高并发爬虫考虑使用异步库MySQL: aiomysqlMongoDB: motor7. 实际项目经验分享在最近的一个舆情监控项目中我们同时使用了MySQL和MongoDBMySQL存储核心元数据文章ID、标题、发布时间等用户关系数据使用InnoDB引擎建立合适索引MongoDB存储原始HTML内容清洗后的结构化数据JSON格式情感分析结果这样的混合架构既保证了核心数据的关系完整性又保留了原始数据的灵活性。一个实际教训是一定要在早期设计好数据分片策略当我们的MongoDB数据增长到TB级别时不得不停机重新分片导致服务中断了2小时。对于中小型爬虫项目我的个人建议是数据量10GB单机MySQL足够数据量10GB-1TB考虑MySQL主从MongoDB分片数据量1TB需要专业的分布式存储方案最后提醒一点无论使用哪种数据库都要记得定期备份我曾经因为服务器故障丢失过一周的爬取数据现在设置了每天凌晨3点的自动备份任务。可以使用mysqldump或mongodump工具这里给出一个简单的备份脚本示例#!/bin/bash # MySQL备份 mysqldump -u root -p123456 --all-databases /backups/mysql_$(date %Y%m%d).sql # MongoDB备份 mongodump --out /backups/mongodb_$(date %Y%m%d)希望这些实战经验能帮你少走弯路。如果遇到其他具体问题欢迎交流讨论。记住数据库操作一定要先在小规模数据上测试确认无误后再应用到生产环境。