Scrapy Pipeline之与数据库交互
来源:互联网 发布:java 编码格式 编辑:程序博客网 时间:2024/05/27 20:19
有很多数据库符合Python Database API规范2.0,例如MySQL,PostgreSQL,Orcale,Microsoft SQL Server和SQLite。它们的驱动是很复杂的并且经过了很多测试,如果再为了Twisted重新实现就太浪费了。你可以在Twisted应用中使用这些数据库客户端,例如,Scrapy就使用了twisted.enterprise.adbapi
库。我们会用MySQL来展示一下如何使用,不过相同的使用方法可以应用到其他兼容的数据库上。
写入MySQL的Pipeline
先来创建一个数据库:
mysql> create database properties;mysql> use propertiesmysql> CREATE TABLE properties ( url varchar(100) NOT NULL, title varchar(30), price DOUBLE, description varchar(30), PRIMARY KEY (url));mysql> SELECT * FROM properties LIMIT 10;Empty set (0.00 sec)
这样就创建了一个MySQL数据库和一个叫做properties的表格,里面有一些准备用来创建pipeline的字段。
接下来会使用MySQL的Python客户端,并要安装一个模块叫做dj-database-url
,它是用来帮助我们解析连接到数据库的URL的,可以使用pip install dj-database-url MySQL-python
来安装。下面是MySQL Pipeline的代码:
import tracebackimport dj_database_urlimport MySQLdbfrom twisted.internet import deferfrom twisted.enterprise import adbapifrom scrapy.exceptions import NotConfiguredclass MysqlWriter(object): """ A spider that writes to MySQL databases """ @classmethod def from_crawler(cls, crawler): """Retrieves scrapy crawler and accesses pipeline's settings""" # Get MySQL URL from settings mysql_url = crawler.settings.get('MYSQL_PIPELINE_URL', None) # If doesn't exist, disable the pipeline if not mysql_url: raise NotConfigured # Create the class return cls(mysql_url) def __init__(self, mysql_url): """Opens a MySQL connection pool""" # Store the url for future reference self.mysql_url = mysql_url # Report connection error only once self.report_connection_error = True # Parse MySQL URL and try to initialize a connection conn_kwargs = MysqlWriter.parse_mysql_url(mysql_url) self.dbpool = adbapi.ConnectionPool('MySQLdb', charset='utf8', use_unicode=True, connect_timeout=5, **conn_kwargs) def close_spider(self, spider): """Discard the database pool on spider close""" self.dbpool.close() @defer.inlineCallbacks def process_item(self, item, spider): """Processes the item. Does insert into MySQL""" logger = spider.logger try: yield self.dbpool.runInteraction(self.do_replace, item) except MySQLdb.OperationalError: if self.report_connection_error: logger.error("Can't connect to MySQL: %s" % self.mysql_url) self.report_connection_error = False except: print traceback.format_exc() # Return the item for the next stage defer.returnValue(item) @staticmethod def do_replace(tx, item): """Does the actual REPLACE INTO""" sql = """REPLACE INTO properties (url, title, price, description) VALUES (%s,%s,%s,%s)""" args = ( item["url"][0][:100], item["title"][0][:30], item["price"][0], item["description"][0].replace("\r\n", " ")[:30] ) tx.execute(sql, args) @staticmethod def parse_mysql_url(mysql_url): """ Parses mysql url and prepares arguments for adbapi.ConnectionPool() """ params = dj_database_url.parse(mysql_url) conn_kwargs = {} conn_kwargs['host'] = params['HOST'] conn_kwargs['user'] = params['USER'] conn_kwargs['passwd'] = params['PASSWORD'] conn_kwargs['db'] = params['NAME'] conn_kwargs['port'] = params['PORT'] # Remove items with empty values conn_kwargs = dict((k, v) for k, v in conn_kwargs.iteritems() if v) return conn_kwargs
parse_mysql_url()
函数用来把MYSQL_PIPELINE_URL
设置的值解析成单个的参数并在__init__()
函数中把它传递给adbapi.ConnectPool()
,由这个函数使用adbapi
来对MySQL连接池进行初始化。它的第一个参数是需要加载的模块,在此例中是MySQLdb
。我们为MySQL客户端设置了一些其他的参数来处理Unicode和超时。所有的这些参数都会在adbapi
需要打开新的连接时传递给MySQLdb.connect()
函数。爬虫关闭的时候,调用close()
函数。
process_item()
方法只是对dbpool.runInteraction()
的一个包装。这个方法把回调函数组织成一个队列并在连接池中的某个连接的Transaction
对象可用时调用回调函数。Transaction
对象有一个和DB-API游标类似的API。在这个例子中,回调函数是do_replace()
,定义在后面几行。@staticmethod
意味着这个方法是与类相关的而不是与类的实例相关的,其实可以删除前面的self
参数。如果一个方法中没有使用成员变量,那么最好还是把它设置成静态方法,但是忘记这样做也无所谓。这个方法准备了一个SQL语句,一些参数,并调用了Transaction
的execute()
方法来执行插入操作。方法中的SQL语句使用了REPLACE INTO
而不是INSERT INTO
,以便当一个条目已经存在时来替换它。如果需要SQL语句返回结果,比如使用了SELECT
语句,那就要使用dbpool.runQuery()
。通过设置adbapi.ConnectionPool()
方法的cursorclass
参数还可以改变默认的游标,比如cursorclass=MySQLdb.cursors.DictCursor
,因为它更利于数据的检索。
在ITEM_PIPELINES
中加上:
ITEM_PIPELINES = { ...'properties.pipelines.mysql.MysqlWriter': 700,...}MYSQL_PIPELINE_URL = 'mysql://root:pass@mysql/properties'
执行下面的命令:
scrapy crawl easy -s CLOSESPIDER_ITEMCOUNT=1000
运行之后查看MySQL的记录:
mysql> SELECT COUNT(*) FROM properties;+----------+| 1006 |+----------+mysql> SELECT * FROM properties LIMIT 4;+------------------+--------------------------+--------+-----------+| url | title | price | description+------------------+--------------------------+--------+-----------+| http://...0.html | Set Unique Family Well | 334.39 | website c| http://...1.html | Belsize Marylebone Shopp | 388.03 | features| http://...2.html | Bathroom Fully Jubilee S | 365.85 | vibrant own| http://...3.html | Residential Brentford Ot | 238.71 | go court+------------------+--------------------------+--------+-----------+4 rows in set (0.00 sec)
至于性能和之前的一样。
- Scrapy Pipeline之与数据库交互
- scrapy爬虫之Image Pipeline
- scrapy爬虫之Item Pipeline
- scrapy pipeline 同步和异步写入数据库
- Scrapy Pipeline之使用REST API
- 爬虫框架Scrapy之Item Pipeline
- Scrapy Pipeline之处理CPU密集型或阻塞型操作
- scrapy 的 item pipeline
- netty之Channel与Pipeline
- 爬虫Scrapy-04Item Pipeline
- Scrapy入门教程之写入数据库
- Scrapy Pipeline之使用专门的Twisted客户端(以Redis缓存为例)
- Python爬虫从入门到放弃(十六)之 Scrapy框架中Item Pipeline用法
- xml与数据库交互
- flex与数据库交互
- flex与数据库交互
- flex与数据库交互
- ajax与数据库交互
- poj 1860 Currency Exchange
- (转)Android屏幕适配全攻略(最权威的官方适配指导)
- POj-3104 Drying 二分+贪心
- (java)关于OJ中字符串通过空格分隔的情况,注意事项
- 并行计算中的BSP模型
- Scrapy Pipeline之与数据库交互
- java 路径(相对路径和绝对路径)问题汇总【转】
- Java 使用Socket实现文件上传与下载
- 如何在sublime里编译latex文档(+部分不常见问题及解决办法)
- oracle数据库FAQ
- javascript之typeof、constructor、instanceof
- Android AutoLayout全新的适配方式
- 100条经典C语言笔试题目(上)
- 在windows下使用Charles对移动终端抓包分析