本文通过爬取jobbole伯乐网讲解scrapy爬虫的三个使用小技巧,包括如何编写脚本执行爬虫,在pipeline使用twisted异步数据入库以及在item容器中预处理爬取到的字段。
爬取的域名是:
blog.jobbole.com
首先,在settings,py中将
ROBOTSTXT_OBEY = True
改为False
或者如果他是注释掉的,要将他的注释去掉,再改为False,否则他就会去查看每个网页是否有robots.txt协议,百度就是有这个东西,并在协议中告知禁止爬取,所以scrapy无法爬取百度。
只要我们把这项改为False他就不会查看这个东西,就能爬取。
建立了jobbl这个项目。
然后cmd进入到这个项目的spiders目录
scrapy genspider jobbole blog.jobbole.com
就会在spiders目录创建一个spider文件
命令格式:
scrapy genspider 蜘蛛名 域名
接下来是一个很重要的小技巧,为了方便调试,我们不在cmd执行scrapy crawl
而是写一个main.py文件(放在和scrapy.cfg同级目录),并在这个文件中执行。
main.py 如下:
解释:
execute()方法的作用是模拟cmd命令行执行命令,用空格隔开的每一项就作为execute参数列表的一个元素。
由于scrapy crawl jobbole这个命令必须要在项目目录jobbole下运行,所以将这个路径添加到sys中。
os.path.abspath(__file__)可以获取当前main.py的绝对路径,包含文件名,然后os.path.dirname是获取路径的目录名
在运行main.py之前,在jobboleSpider.py中的def parse()的下一行打一个断点(点击左侧行号即可),然后用alt+shift+F9运行main.py(这个是以调试模式运行py)
运行了之后,回到蜘蛛文件,用鼠标点击一下parse()中的response变量,再点一下他跳出来的绿色的+号,就可以看到response对象中的所有属性和方法
这里再补充一点xpath语法知识:
//div/a|//ul 这样表示获取所有div下的a元素或者或者所有ul元素。就是都能获取到的意思
还有,有的时候一个元素的class有多个,比如是a b c这3个类名。
如果我写成
//div[@class="b"]
他是无法获取到这个元素的
必须写成
//div[@class="a b c"] 才可以
但是还有一个方法:
//div[contains(@class,"b")]
意思是只要class属性中包含 b 这个字符串即可获取到这个元素了
现在,我们不用xpath的方式爬,而是使用css选择器来爬:
比如:
response.css(".entry h1").extract()
response.css(".entry h1")返回的还是selector列表对象,extract是返回节点列表
如果想要获取里面的文本:
response.css(".entry h1::text").extract()
如果想获取a标签的href可以:
response.css("a::attr(href)").extract()
这里我再说一点小技巧,把信息转为json的时候
json.dumps(info)
如果你不希望里面的中文变成\u0e13b4这样的东西,你可以这样:
json.dumps(info,ensure_ascii=False)
=========================================
项目编写过程如下:
建表
settings.py配置数据库信息:
DB_CONF={
"host":"127.0.0.1",
"user":"root",
"password":"你的数据库密码",
"database":"test",
"charset":"utf8",
"cursorclass":pymysql.cursors.DictCursor
}
并开启pipeline
接下来是第二个重要的小技巧:在pipeline.py中进行数据异步存储
spider如下:
我们在item中对各个字段做预处理:
这里我要说一下,字段获取到之后他会先经过JobblItem的处理之后再返回给蜘蛛,也就是说,spider中的parseDetail中的 return item 中的item是已经处理好了的字段。
PS:回调函数必须放在MapCompose调用前定义,例如上面的strip_space就要放在最上面就定义好.否则会报错
因为我们每一个字段都要经过output_processor=TakeFirst()处理,所以我们可以这样:
在items.py中定义一个类继承ItemLoader类,在这个类中重写一个属性default_output_processor = TakeFirst()
这样就不用每一个字段都写output_processor = TakeFirst()这一句了。
然后在spider里面引入JobblItemLoader,实例化容器只需
from jobbl.items import JobblItemLoader,JobblItem
...
item_loader = JobblItemLoader(item=JobblItem(),response=response)
总结一下:
1. 在scrapy项目的根目录下编写main.py脚本,通过运行该脚本执行蜘蛛,可以避免在命令行中运行蜘蛛的麻烦而且方便调试
2.在pipeline将数据入库的时候,使用twisted.enterprise.adbapi建立数据库连接池,将数据入库异步化可以提高爬虫的爬取速度,不必等待数据入库这个过程和时间
3.使用scrapy.loader.ItemLoader和scrapy.loader.processors实现数据处理,将这一过程从spider文件和pipeline文件分离,使代码看起来更加简洁清晰有层次