python 爬虫框架scrapy 基础入门笔记(后续跟基础入门代码)

一、scrapy安装和配置

1、pip install scrapy

 很多安装容易出错的包,可以直接下载安装https://www.lfd.uci.edu/~gohike/pythonlibs

2、scrapy web抓取框架,底部异步io框架,事件循环+回调模式。尽量不要使用同步io。

3、常见命令:

scrapy startproject AricleSpider  创建scrapy项目

4、目录结构 

spiders

items

middlewares

pipelines

settings

5、创建自定义spider

scrapy genspider jobbole news.cnblogs.com  

 

二、pycharm调试scrapy

1、scrapy crawl jobbole 启动爬虫

或者:

脚本启动爬虫:代码中启动根目录建main.py 

from scrapy.cmdline import  execute

sys.path.append(os.path.dirname(__file__))

execute(["scrapy","crawl","jobbole"])

 

2、启动程序后调用start_urls  调用parse()

 

三、xpath语法

1、节点关系

article 选取所有元素的所有子节点

/article 选取根元素article

article/a article的子元素a元素

//div 所有div子元素(无论出现在哪里)

article//div article所有后代div,无论出现在哪里

//@class 所有名为class的属性

/article/div[1] article子元素第一个div元素

/article/div[last()] 子元素第一个div元素

/article/div[last()-1] 属于article子元素的倒数第二个div元素

/article[@lang] 选取拥有lang属性的div元素

//div[@lang='eng'] 选取所以lang属性为eng的div元素

/div/* div元素的所有子节点

//* 所有元素

//div[@*] 所有带属性的title元素

/div/a|//div/p  选取所有div元素的a和p元素

//span|//ul 选取文档总span和ul元素

article/div/p|//span   属于article元素的div元素的p元素和所有span元素

 

2、scrapy中通过xpath获取需要的元素

selectorList = response.xpath('xxx').extract_first("")

 

四、css选择器(scrapy支持xpath和css选择器)

*所有节点

#container  选择id为container的节点

.container 所有class包含container的节点

li a 所有li下所有a节点

ul + p   ul后面第一个p元素

div#container > ul  id为container的div的ul子元素

p~ul 前面有p元素的每个ul元素

a[title] 所有属性为title的a元素

a[href="http://jobbole.com"] 选取所有href属性为jobbole.com的a元素

a[href*="jobole"] 选取所有href属性包含jobbole的暗元素

a[href^="http"] href属性以http开头的a元素

a[href$=".jpg"] 所有href属性以.jpg结尾的a元素

input[type=radio]:checked   选中的radio元素

div:not(#container) id非container的div属性

li:nth-child(3) 第三个li元素

tr:nth-child(2n) 第偶数个tr

2、使用方式:response.css('div#news_list h2 a::attr(href)').extract_first("")

 

3、如果没有response怎么使用xpath和css选择器?

from scrapy import Selector

sel = Selector(test=response.text)

url = sel.css('div#news_list h2 a::attr(href)').extract()

 

五、爬虫抓取

1、parse 一般做抓取策略,并不做解析  (这里获取列表中新闻url,并交给scrapy下载后调用解析方法)

2、response.css 返回时Selector   可直接调用css。

3、parse.urljoin(response.url,post_url) 如果请求地址是完整的url不会提取域名,如果不是会提取url域名。

4、parse里  yield Request(url=parse.urljoin(response.url,post_url),meta={"front_image_url":image_url},callback=parse_detail)

 

5、源码中start_requests里 有dont_filter,会去重,可以设置不过滤。

6、scrapy是异步框架,下载完成后才进入parse_detail

 

六、提取页面详情页

1、scrapy shell  测试使用。如scrapy shell https://news.cnblogs.com/n/643059   ,可以直接用xpath测试

2、有些数据在html里提取不到,可能是js加载,需要通过请求查找。

3、尽量不要使用同步库,如requests,可以通过yield 把请求发送出去(回调模式)

4、传递参数,通过meta传递

 七、scrapy数据传递方式:

items:默认生成范例,可以写item,继承scrapy.Item,可以简单理解为dict。

定义需要解析的字段,如:title = scrapy.Field()   可以理解为这里的字段都会存储在数据库内。

yield只能 返回Request(走下载逻辑)和Item(走pipline),

 

八、settings里的ROBOTSTXT_OBEY如果设置为True,则网站的robots里约束的不会爬,一般改为Fales

settings里有pipeline,一般注释,可以打开,数字是设置优先级,越小越先执行。打开后会进入pipeline,items定义的值会带过来,所以这里可以做数据保存和处理。

 

九、如果让scrapy自动下载文件?

settings加配置:

1、'scrapy.pipelines.images.ImagesPipeline': 1,

2、增加配置路径:IMAGES_STORE = os.path.join(os.path.dirname(os.path.abspath(__file__)),'images')

3、IMAGES_URLS_FIELD = 'front_image_url'  图片路径名的配置

4、front_image_url必须是一个list 

如果需要存储对图片本地路径进行存储,可以重写pipline

class AricleImagePipeline(ImagesPipeline):

# 把下载图片和对应的本地地址放在一个对象中

def item_completed(self, results, item, info):

if 'front_image_url' in item:

for ok,value in results:

# value 存放图片url和本地存储path

image_file_path = value["path"]

item["front_image_path"] = image_file_path

return item

并修改settings

 

十、把数据保存在本地json

class JsonWithEncodingPipeline(object):

# 自定义json文件的导出

def __init__(self):

self.file = codecs.open('article.json', 'w', encoding='utf-8')

 

def process_item(self,item,spider):

lines = json.dumps(dict(item),ensure_ascii=False) + '\n'

self.file.write(lines)

return item

def spider_closed(self, spider):

self.file.close()

 

十一、scrapy自带 存储json的方法:

 

class JsonExporterPipline(object):

# 系统方法导出json数据

def __init__(self):

self.file = codecs.open('articleexport.json', 'wb', encoding='utf-8')

self.exporter = JsonItemExporter(self.file,encoding='utf-8',ensure_ascii=False)

self.export.start_exporting()

 

def process_item(self,item,spider):

self.exporter.export_item(item)

return item

def spider_closed(self, spider):

self.exporter.finish_exporting()

self.file.close()

 

十二、同步入库mysql

 

class MysqlPipline(object):

def __init__(self):

self.conn = MySQLdb.connect('127.0.0.1', 'root', 'root', 'article_spider', charset='utf8', use_unicode=True)

self.cursor = self.conn.cursor()

 

def process_item(self, item, spider):

insert_sql = '''

insert into jobbole_article(title,url,url_object_id,front_image_url,front_image_path,parise_nums,comment_nums,fav_nums,tags,content,creat_date)

values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)

'''

params = list()

params.append(item.get('title'))

params.append(item.get('url'))

params.append(item.get('url_object_id'))

from_image = ','.join(item.get('front_image_url',''))

params.append(from_image)

params.append(item.get('front_image_path'))

params.append(item.get('parise_nums',0))

params.append(item.get('comment_nums',0))

params.append(item.get('fav_nums',0))

params.append(item.get('tags',''))

params.append(item.get('content',''))

params.append(item.get('creat_date','1970-07-01'))

self.cursor.execute(insert_sql,tuple(params))

self.conn.commit()

return item

 

十三、异步入库mysql(通用方式,可以直接处理)

class MysqlTwistedPipline(object):

def __init__(self, dbpool):

self.dbpool = dbpool

 

@classmethod

def from_settings(cls, settings):

from MySQLdb.cursors import DictCursor

dbparms = dict(

host=settings['MYSQL_HOST'],

db=settings['MYSQL_DBNAME'],

user=settings['MYSQL_USER'],

passwd=settings['MYSQL_PASSWORD'],

charset='utf8',

cursorclass=DictCursor,

use_unicode=True

)

dbpool = adbapi.ConnectionPool('MySQLdb', **dbparms)

return cls(dbpool)

 

def process_item(self, item, spider):

query = self.dbpool.runInteraction(self.do_insert, item)

query.addErrback(self.handle_error, item, spider)

 

def handle_error(self, failure, item, spider):

print(failure)

 

def do_insert(self, cursor, item):

insert_sql = '''

insert into jobbole_article(title,url,url_object_id,front_image_url,front_image_path,parise_nums,comment_nums,fav_nums,tags,content,creat_date)

values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)

'''

params = list()

params.append(item.get('title'))

params.append(item.get('url'))

params.append(item.get('url_object_id'))

from_image = ','.join(item.get('front_image_url', ''))

params.append(from_image)

params.append(item.get('front_image_path'))

params.append(item.get('parise_nums', 0))

params.append(item.get('comment_nums', 0))

params.append(item.get('fav_nums', 0))

params.append(item.get('tags', ''))

params.append(item.get('content', ''))

params.append(item.get('creat_date', '1970-07-01'))

cursor.execute(insert_sql, tuple(params))

十四、数据库主键冲突处理方式:

添加on DUPLICATE KEY UPDATE

INSERT INTO jobbole_article

(front_image_url, create_date,image_url_id,title,content,tags,content_id,comment_count,total_view,digg_count,bury_count)

VALUES

("{}","{}","{}","{}",'{}',"{}",{},{},{},{},{}) on DUPLICATE KEY UPDATE parise_nums=VALUES(bury_count);;

 

十五、ItemLoader用法,可以让代码简洁

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章