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用法,可以讓代碼簡潔

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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