scrapy框架使用教程
scrapy框架真的是很強大。非常值得學習一下。本身py就追求簡潔,所以本身代碼量很少卻能寫出很強大的功能。對比java來說。不過py的語法有些操蛋,比如沒有智能提示。動態語言的通病。我也剛學習不到1周時間。記錄一下。全部干貨。
首先安裝scrapy框架。選擇的ide是pycharm。
創建一個scrapy項目。項目名稱xxoo
scrapy startproject xxoo會得到一個項目目錄。具體目錄的作用自己百度下。然后再用一條命令創建一個爬蟲類。就是一個模板。幫我們創建好的類。我們只需要寫邏輯就行。程序員的天性就是懶!!!
意思是創建了一個xxooSpider的類? 這個類只爬取baidu.com這個網站
scrapy genspider [-t template] <name> <domain> 即:scrapy genspider xxooSpider baidu.com在pycharm中調試項目。
需要特殊配置下。
在根目錄下創建一個start.py的文件。?-o itcast1.csv? 是輸出到csv文件中。可以不加
from scrapy import cmdline cmdline.execute("scrapy crawl xxooSpider --nolog -o itcast1.csv".split())就ok了。
?使用豆瓣鏡像源下載
pip install -i https://pypi.doubanio.com/simple/ scrapy-splash?
獲取setting.py中的值
from scrapy.conf import settingscookie = settings['COOKIE']?獲取圖片的url地址
大牛通常使用這個方法。原因是,我們一般情況下也可以直接得到src屬性的值。但是,有時候src屬性的值沒有帶網址前綴,比如說是/img/1.png這樣。我們需要手動加上http://www.baidu.com才可以。用下面這個方法。可以很簡單的解決這個問題。
from urllib import parseurl="http://www.baidu.com/xx" xx="/pic/1/1.png"urljoin = parse.urljoin(url, xx) print(urljoin)http://www.baidu.com/pic/1/1.png
下載圖片
scrapy給我們提供好了圖片下載的模板。我們只需要在setting中指定一下管道中間件,和需要下載的字段。需要下載的字段值一定是數組類型,不然報錯
ITEM_PIPELINES = {'xxoo.pipelines.XxooPipeline': 300,'scrapy.pipelines.images.ImagesPipeline': 1, } #在item中定義圖片url的字段,ImagesPipeline會自動下載這個url地址 IMAGES_URLS_FIELD="image" #存放的路徑,根目錄下的img文件夾 IMAGES_STORE=os.path.join(os.path.abspath(os.path.dirname(__file__)),"img")但是按照上面的寫的話,全部都是由scrapy幫我們做了,自己生成文件夾,文件名。非常不可控。如果我們想自定義的話。我們需要繼承ImagesPipeline類,重寫幾個方法
from scrapy.pipelines.images import ImagesPipeline import re from scrapy import Requestclass ImagesrenamePipeline(ImagesPipeline):# 1看源碼可以知道,這個方法只是遍歷出我們指定的圖片字段,是個數組,然后一個一個請求def get_media_requests(self, item, info):# 循環每一張圖片地址下載,若傳過來的不是集合則無需循環直接yieldfor image_url in item['imgurl']:# meta里面的數據是從spider獲取,然后通過meta傳遞給下面方法:file_pathyield Request(image_url,meta={'name':item['imgname']})# 2重命名,若不重寫這函數,圖片名為哈希,就是一串亂七八糟的名字def file_path(self, request, response=None, info=None):# 提取url前面名稱作為圖片名。image_guid = request.url.split('/')[-1]# 接收上面meta傳遞過來的圖片名稱name = request.meta['name']# 過濾windows字符串,不經過這么一個步驟,你會發現有亂碼或無法下載name = re.sub(r'[?\\*|“<>:/]', '', name)# 分文件夾存儲的關鍵:{0}對應著name;{1}對應著image_guidfilename = u'{0}/{1}'.format(name, image_guid)return filename#3這個是請求完成之后走的方法,我們可以得到請求的url和存放的地址def item_completed(self, results, item, info):pass保存item到json文件
自定義的
import codecs import json class jsonwrite(object):# 初始化,打開文件def __init__(self):self.file = codecs.open("xxoo.json", "w",encoding="utf-8")# scrapy會走這個方法進行item的寫入def process_item(self,item,spider):self.file.write(json.dumps(dict(item),ensure_ascii=False) + "\n")# 通常是關閉文件的操作def spider_closed(self,spider):self.file.close()scrapy給我們提供的
from scrapy.exporters import JsonItemExporter class JsonExporterPipleline(object):#調用scrapy提供的json export導出json文件def __init__(self):self.file = open('articleexport.json', 'wb')self.exporter = JsonItemExporter(self.file, encoding="utf-8", ensure_ascii=False)self.exporter.start_exporting()def close_spider(self, spider):self.exporter.finish_exporting()self.file.close()def process_item(self, item, spider):self.exporter.export_item(item)return item保存到mysql中(兩種方法)
import MySQLdb import MySQLdb.cursors from twisted.enterprise import adbapiclass MysqlPipeline(object):#采用同步的機制寫入mysqldef __init__(self):self.conn = MySQLdb.connect('192.168.0.106', '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, create_date, fav_nums)VALUES (%s, %s, %s, %s)"""self.cursor.execute(insert_sql, (item["title"], item["url"], item["create_date"], item["fav_nums"]))self.conn.commit()#采用異步數據庫連接池的方法 class MysqlTwistedPipline(object):def __init__(self, dbpool):self.dbpool = dbpool@classmethoddef from_settings(cls, settings):dbparms = dict(host = settings["MYSQL_HOST"],db = settings["MYSQL_DBNAME"],user = settings["MYSQL_USER"],passwd = settings["MYSQL_PASSWORD"],charset='utf8',cursorclass=MySQLdb.cursors.DictCursor,use_unicode=True,)dbpool = adbapi.ConnectionPool("MySQLdb", **dbparms)return cls(dbpool)def process_item(self, item, spider):#使用twisted將mysql插入變成異步執行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):#執行具體的插入#根據不同的item 構建不同的sql語句并插入到mysql中insert_sql, params = item.get_insert_sql()cursor.execute(insert_sql, params)?優化item類(重要)
我們可以用xpath或者css解析頁面,然后寫一些判斷邏輯。如果你不嫌麻煩的話。
scrapy給我們提供了一整套的流程。可以讓代碼變得非常精簡。處理item的業務邏輯在item中寫。爬蟲文件只寫item的生成規則。
先看item類
from scrapy.loader import ItemLoader from scrapy.loader.processors import MapCompose, TakeFirst, Join#一個小技巧,可以覆蓋默認的規則,就是TakeFirst()把列表轉換成字符串,我們這里不讓轉成字符串,還是數組 def return_value(value):return value#因為通過自帶的ItemLoader類生成的item_loader他都是list,所以我們自定義下。默認的處理規則(可以單個字段覆蓋),這樣就不用每個字段都寫重復的代碼了 class ArticleItemLoader(ItemLoader):#自定義itemloaderdefault_output_processor = TakeFirst()#自定義的item類。input_processor是指需要處理的業務邏輯,比如一些格式的轉換什么的,output_processor可以覆蓋默認的規則。 class JobBoleArticleItem(scrapy.Item):title = scrapy.Field()create_date = scrapy.Field(input_processor=MapCompose(date_convert),)url = scrapy.Field()url_object_id = scrapy.Field()front_image_url = scrapy.Field(output_processor=MapCompose(return_value))front_image_path = scrapy.Field()praise_nums = scrapy.Field(input_processor=MapCompose(get_nums))comment_nums = scrapy.Field(input_processor=MapCompose(get_nums))fav_nums = scrapy.Field(input_processor=MapCompose(get_nums))tags = scrapy.Field(input_processor=MapCompose(remove_comment_tags),output_processor=Join(","))content = scrapy.Field()爬蟲類
from scrapy.loader import ItemLoader from ArticleSpider.items import JobBoleArticleItem, ArticleItemLoaderdef parse_detail(self, response):article_item = JobBoleArticleItem()#通過item loader加載itemfront_image_url = response.meta.get("front_image_url", "") # 文章封面圖item_loader = ArticleItemLoader(item=JobBoleArticleItem(), response=response)item_loader.add_css("title", ".entry-header h1::text")item_loader.add_value("url", response.url)item_loader.add_value("url_object_id", get_md5(response.url))item_loader.add_css("create_date", "p.entry-meta-hide-on-mobile::text")item_loader.add_value("front_image_url", [front_image_url])item_loader.add_css("praise_nums", ".vote-post-up h10::text")item_loader.add_css("comment_nums", "a[href='#article-comment'] span::text")item_loader.add_css("fav_nums", ".bookmark-btn::text")item_loader.add_css("tags", "p.entry-meta-hide-on-mobile a::text")item_loader.add_css("content", "div.entry")article_item = item_loader.load_item()yield article_item?獲取一個頁面的全部url
我們當然可以用xpath得到,但是還不夠精簡。我們可以使用? ?linkExtractor? 類來得到。非常的簡單。
from scrapy.linkextractors import LinkExtractor# 需要搞一個對象實例,然后寫一個符合的規則,利用extract_links方法傳一個response過去就能得到這個頁面匹配的url link = linkExtractor=LinkExtractor(allow=r'http://lab.scrapyd.cn')# link = linkExtractor=LinkExtractor()#allow=r'http://lab.scrapyd.cn/archives/\d+.html' links = link.extract_links(response) if links: for link_one in links: print(link_one)?日志的使用
Scrapy提供了log功能,可以通過 logging 模塊使用。
可以修改配置文件settings.py,任意位置添加下面兩行,效果會清爽很多。
LOG_FILE = "TencentSpider.log" LOG_LEVEL = "INFO" Log levels Scrapy提供5層logging級別:CRITICAL - 嚴重錯誤(critical)ERROR - 一般錯誤(regular errors) WARNING - 警告信息(warning messages) INFO - 一般信息(informational messages) DEBUG - 調試信息(debugging messages)logging設置
通過在setting.py中進行以下設置可以被用來配置logging:
LOG_ENABLED 默認: True,啟用logging LOG_ENCODING 默認: 'utf-8',logging使用的編碼 LOG_FILE 默認: None,在當前目錄里創建logging輸出文件的文件名 LOG_LEVEL 默認: 'DEBUG',log的最低級別 LOG_STDOUT 默認: False 如果為 True,進程所有的標準輸出(及錯誤)將會被重定向到log中。例如,執行 print "hello" ,其將會在Scrapy log中顯示。?保存到mongdb數據庫
import pymongo from scrapy.conf import settingsclass DoubanPipeline(object):def __init__(self):host = settings["MONGODB_HOST"]port = settings["MONGODB_PORT"]dbname = settings["MONGODB_DBNAME"]sheetname= settings["MONGODB_SHEETNAME"]# 創建MONGODB數據庫鏈接client = pymongo.MongoClient(host = host, port = port)# 指定數據庫mydb = client[dbname]# 存放數據的數據庫表名self.sheet = mydb[sheetname]def process_item(self, item, spider):data = dict(item)self.sheet.insert(data)return itemsetting文件
# MONGODB 主機名 MONGODB_HOST = "127.0.0.1"# MONGODB 端口號 MONGODB_PORT = 27017# 數據庫名稱 MONGODB_DBNAME = "Douban"# 存放數據的表名稱 MONGODB_SHEETNAME = "doubanmovies"?下載中間件,隨機更換user-Agent和ip
import random import base64from settings import USER_AGENTS from settings import PROXIES# 隨機的User-Agent class RandomUserAgent(object):def process_request(self, request, spider):useragent = random.choice(USER_AGENTS)#print useragentrequest.headers.setdefault("User-Agent", useragent)class RandomProxy(object):def process_request(self, request, spider):proxy = random.choice(PROXIES)if proxy['user_passwd'] is None:# 沒有代理賬戶驗證的代理使用方式request.meta['proxy'] = "http://" + proxy['ip_port']else:# 對賬戶密碼進行base64編碼轉換base64_userpasswd = base64.b64encode(proxy['user_passwd'])# 對應到代理服務器的信令格式里request.headers['Proxy-Authorization'] = 'Basic ' + base64_userpasswdrequest.meta['proxy'] = "http://" + proxy['ip_port']setting文件
USER_AGENTS = ['Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0)','Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2)','Opera/9.27 (Windows NT 5.2; U; zh-cn)','Opera/8.0 (Macintosh; PPC Mac OS X; U; en)','Mozilla/5.0 (Macintosh; PPC Mac OS X; U; en) Opera 8.0','Mozilla/5.0 (Linux; U; Android 4.0.3; zh-cn; M032 Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30','Mozilla/5.0 (Windows; U; Windows NT 5.2) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13' ]PROXIES = [{"ip_port" :"121.42.140.113:16816", "user_passwd" : "mr_mao_hacker:sffqry9r"},#{"ip_prot" :"121.42.140.113:16816", "user_passwd" : ""}#{"ip_prot" :"121.42.140.113:16816", "user_passwd" : ""}#{"ip_prot" :"121.42.140.113:16816", "user_passwd" : ""} ]?登陸的三種方法
1,直接找到登陸接口,提供賬號密碼進行登陸,也是最簡單的。
2,有時候需要從登錄頁找到隱藏的值,然后提交到后臺,比如知乎就需要在登錄頁得到_xsrf,
3,最麻煩的一種,對方各種加密驗證,我們可以采用cookie進行登陸。
分別寫三個代碼參考下:
1.簡單
# -*- coding: utf-8 -*- import scrapy# 只要是需要提供post數據的,就可以用這種方法, # 下面示例:post數據是賬戶密碼 class Renren1Spider(scrapy.Spider):name = "renren1"allowed_domains = ["renren.com"]def start_requests(self):url = 'http://www.renren.com/PLogin.do'yield scrapy.FormRequest(url = url,formdata = {"email" : "mr_mao_hacker@163.com", "password" : "alarmchime"},callback = self.parse_page)def parse_page(self, response):with open("mao2.html", "w") as filename:filename.write(response.body) View Code2.中等
# -*- coding: utf-8 -*- import scrapy# 正統模擬登錄方法: # 首先發送登錄頁面的get請求,獲取到頁面里的登錄必須的參數,比如說zhihu的 _xsrf # 然后和賬戶密碼一起post到服務器,登錄成功class Renren2Spider(scrapy.Spider):name = "renren2"allowed_domains = ["renren.com"]start_urls = ("http://www.renren.com/PLogin.do",)def parse(self, response):#_xsrf = response.xpath("//_xsrf").extract()[0]yield scrapy.FormRequest.from_response(response,formdata = {"email" : "mr_mao_hacker@163.com", "password" : "alarmchime"},#, "_xsrf" = _xsrf},callback = self.parse_page)def parse_page(self, response):print "=========1===" + response.url#with open("mao.html", "w") as filename:# filename.write(response.body)url = "http://www.renren.com/422167102/profile"yield scrapy.Request(url, callback = self.parse_newpage)def parse_newpage(self, response):print "===========2====" + response.urlwith open("xiao.html", "w") as filename:filename.write(response.body) View Code3.困難
# -*- coding: utf-8 -*- import scrapy# 實在沒辦法了,可以用這種方法模擬登錄,麻煩一點,成功率100%class RenrenSpider(scrapy.Spider):name = "renren"allowed_domains = ["renren.com"]start_urls = ('http://www.renren.com/xxxxx','http://www.renren.com/11111','http://www.renren.com/xx',)cookies = {"anonymid" : "ixrna3fysufnwv","_r01_" : "1","ap" : "327550029","JSESSIONID" : "abciwg61A_RvtaRS3GjOv","depovince" : "GW","springskin" : "set","jebe_key" : "f6fb270b-d06d-42e6-8b53-e67c3156aa7e%7Cc13c37f53bca9e1e7132d4b58ce00fa3%7C1484060607478%7C1%7C1486198628950","jebe_key" : "f6fb270b-d06d-42e6-8b53-e67c3156aa7e%7Cc13c37f53bca9e1e7132d4b58ce00fa3%7C1484060607478%7C1%7C1486198619601","ver" : "7.0","XNESSESSIONID" : "e703b11f8809","jebecookies" : "98c7c881-779f-4da8-a57c-7464175cd469|||||","ick_login" : "4b4a254a-9f25-4d4a-b686-a41fda73e173","_de" : "BF09EE3A28DED52E6B65F6A4705D973F1383380866D39FF5","p" : "ea5541736f993365a23d04c0946c10e29","first_login_flag" : "1","ln_uact" : "mr_mao_hacker@163.com","ln_hurl" : "http://hdn.xnimg.cn/photos/hdn521/20140529/1055/h_main_9A3Z_e0c300019f6a195a.jpg","t" : "691808127750a83d33704a565d8340ae9","societyguester" : "691808127750a83d33704a565d8340ae9","id" : "327550029","xnsid" : "f42b25cf","loginfrom" : "syshome"}def start_requests(self):for url in self.start_urls:#yield scrapy.Request(url, callback = self.parse)#url = "http://www.renren.com/410043129/profile"yield scrapy.FormRequest(url, cookies = self.cookies, callback = self.parse_page)def parse_page(self, response):print "===========" + response.urlwith open("deng.html", "w") as filename:filename.write(response.body) View Code?
轉載于:https://www.cnblogs.com/coder-lzh/p/9809980.html
總結
以上是生活随笔為你收集整理的scrapy框架使用教程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java hgetall_详解Java使
- 下一篇: 除留余数法学习