假設有四臺電腦:Windows 十、Mac OS X、Ubuntu 16.0四、CentOS 7.2,任意一臺電腦均可以做爲 Master端 或 Slaver端,好比:css
Master端
(核心服務器) :使用 Windows 10,搭建一個Redis數據庫,不負責爬取,只負責url指紋判重、Request的分配,以及數據的存儲html
Slaver端
(爬蟲程序執行端) :使用 Mac OS X 、Ubuntu 16.0四、CentOS 7.2,負責執行爬蟲程序,運行過程當中提交新的Request給Masterpython
首先Slaver端從Master端拿任務(Request、url)進行數據抓取,Slaver抓取數據的同時,產生新任務的Request便提交給 Master 處理;mysql
Master端只有一個Redis數據庫,負責將未處理的Request去重和任務分配,將處理後的Request加入待爬隊列,而且存儲爬取的數據。git
Scrapy-Redis默認使用的就是這種策略,咱們實現起來很簡單,由於任務調度等工做Scrapy-Redis都已經幫咱們作好了,咱們只須要繼承RedisSpider、指定redis_key就好了。github
缺點是,Scrapy-Redis調度的任務是Request對象,裏面信息量比較大(不只包含url,還有callback函數、headers等信息),可能致使的結果就是會下降爬蟲速度、並且會佔用Redis大量的存儲空間,因此若是要保證效率,那麼就須要必定硬件水平。web
安裝Redis:http://redis.io/downloadredis
安裝完成後,拷貝一份Redis安裝目錄下的redis.conf到任意目錄,建議保存到:/etc/redis/redis.conf
(Windows系統能夠無需變更)sql
打開你的redis.conf配置文件,示例:mongodb
非Windows系統: sudo vi /etc/redis/redis.conf
Windows系統:C:\Intel\Redis\conf\redis.conf
Master端redis.conf裏註釋bind 127.0.0.1
,Slave端才能遠程鏈接到Master端的Redis數據庫。
2.daemonize yno
表示Redis默認不做爲守護進程運行,即在運行redis-server /etc/redis/redis.conf
時,將顯示Redis啓動提示畫面;
daemonize yes
則默認後臺運行,沒必要從新啓動新的終端窗口執行其餘命令,看我的喜愛和實際須要。測試中,Master端Windows 10 的IP地址爲:192.168.199.108
Master端按指定配置文件啓動 redis-server
,示例:
非Windows系統:sudo redis-server /etc/redis/redis.conf
Windows系統:命令提示符(管理員)
模式下執行 redis-server C:\Intel\Redis\conf\redis.conf
讀取默認配置便可。
Master端啓動本地redis-cli
:
3.slave端啓動redis-cli -h 192.168.199.108
,-h 參數表示鏈接到指定主機的redis數據庫
redis-server
,Master端啓動便可。只要 Slave 端讀取到了 Master 端的 Redis 數據庫,則表示可以鏈接成功,能夠實施分佈式。這裏推薦 Redis Desktop Manager,支持 Windows、Mac OS X、Linux 等平臺:
先從github上拿到scrapy-redis的示例,而後將裏面的example-project目錄移到指定的地址:
# clone github scrapy-redis源碼文件 git clone https://github.com/rolando/scrapy-redis.git # 直接拿官方的項目範例,更名爲本身的項目用(針對懶癌患者) mv scrapy-redis/example-project ~/scrapyredis-project
咱們clone到的 scrapy-redis 源碼中有自帶一個example-project項目,這個項目包含3個spider,分別是dmoz, myspider_redis,mycrawler_redis。
這個爬蟲繼承的是CrawlSpider,它是用來講明Redis的持續性,當咱們第一次運行dmoz爬蟲,而後Ctrl + C停掉以後,再運行dmoz爬蟲,以前的爬取記錄是保留在Redis裏的。
分析起來,其實這就是一個 scrapy-redis 版 CrawlSpider
類,須要設置Rule規則,以及callback不能寫parse()方法。
scrapy crawl dmoz
from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule class DmozSpider(CrawlSpider): """Follow categories and extract links.""" name = 'dmoz' allowed_domains = ['dmoz.org'] start_urls = ['http://www.dmoz.org/'] rules = [ Rule(LinkExtractor( restrict_css=('.top-cat', '.sub-cat', '.cat-item') ), callback='parse_directory', follow=True), ] def parse_directory(self, response): for div in response.css('.title-and-desc'): yield { 'name': div.css('.site-title::text').extract_first(), 'description': div.css('.site-descr::text').extract_first().strip(), 'link': div.css('a::attr(href)').extract_first(), }
這個爬蟲繼承了RedisSpider, 它可以支持分佈式的抓取,採用的是basic spider,須要寫parse函數。
其次就是再也不有start_urls了,取而代之的是redis_key,scrapy-redis將key從Redis裏pop出來,成爲請求的url地址。
from scrapy_redis.spiders import RedisSpider class MySpider(RedisSpider): """Spider that reads urls from redis queue (myspider:start_urls).""" name = 'myspider_redis' # 注意redis-key的格式: redis_key = 'myspider:start_urls' # 可選:等效於allowd_domains(),__init__方法按規定格式寫,使用時只須要修改super()裏的類名參數便可 def __init__(self, *args, **kwargs): # Dynamically define the allowed domains list. domain = kwargs.pop('domain', '') self.allowed_domains = filter(None, domain.split(',')) # 修改這裏的類名爲當前類名 super(MySpider, self).__init__(*args, **kwargs) def parse(self, response): return { 'name': response.css('title::text').extract_first(), 'url': response.url, }
RedisSpider類 不須要寫allowd_domains
和start_urls
:
scrapy-redis將從在構造方法__init__()
裏動態定義爬蟲爬取域範圍,也能夠選擇直接寫allowd_domains
。
必須指定redis_key,即啓動爬蟲的命令,參考格式:redis_key = 'myspider:start_urls'
根據指定的格式,start_urls
將在 Master端的 redis-cli 裏 lpush 到 Redis數據庫裏,RedisSpider 將在數據庫裏獲取start_urls。
經過runspider方法執行爬蟲的py文件(也能夠分次執行多條),爬蟲(們)將處於等待準備狀態:
scrapy runspider myspider_redis.py
在Master端的redis-cli輸入push指令,參考格式:
$redis > lpush myspider:start_urls http://www.dmoz.org/
Slaver端爬蟲獲取到請求,開始爬取。
這個RedisCrawlSpider類爬蟲繼承了RedisCrawlSpider,可以支持分佈式的抓取。由於採用的是crawlSpider,因此須要遵照Rule規則,以及callback不能寫parse()方法。
一樣也再也不有start_urls了,取而代之的是redis_key,scrapy-redis將key從Redis裏pop出來,成爲請求的url地址。
from scrapy.spiders import Rule from scrapy.linkextractors import LinkExtractor from scrapy_redis.spiders import RedisCrawlSpider class MyCrawler(RedisCrawlSpider): """Spider that reads urls from redis queue (myspider:start_urls).""" name = 'mycrawler_redis' redis_key = 'mycrawler:start_urls' rules = ( # follow all links Rule(LinkExtractor(), callback='parse_page', follow=True), ) # __init__方法必須按規定寫,使用時只須要修改super()裏的類名參數便可 def __init__(self, *args, **kwargs): # Dynamically define the allowed domains list. domain = kwargs.pop('domain', '') self.allowed_domains = filter(None, domain.split(',')) # 修改這裏的類名爲當前類名 super(MyCrawler, self).__init__(*args, **kwargs) def parse_page(self, response): return { 'name': response.css('title::text').extract_first(), 'url': response.url, }
一樣的,RedisCrawlSpider類不須要寫allowd_domains
和start_urls
:
scrapy-redis將從在構造方法__init__()
裏動態定義爬蟲爬取域範圍,也能夠選擇直接寫allowd_domains
。
必須指定redis_key,即啓動爬蟲的命令,參考格式:redis_key = 'myspider:start_urls'
根據指定的格式,start_urls
將在 Master端的 redis-cli 裏 lpush 到 Redis數據庫裏,RedisSpider 將在數據庫裏獲取start_urls。
經過runspider方法執行爬蟲的py文件(也能夠分次執行多條),爬蟲(們)將處於等待準備狀態:
scrapy runspider mycrawler_redis.py
在Master端的redis-cli輸入push指令,參考格式:
$redis > lpush mycrawler:start_urls http://www.dmoz.org/
爬蟲獲取url,開始執行。
若是只是用到Redis的去重和保存功能,就選第一種;
若是要寫分佈式,則根據狀況,選擇第二種、第三種;
一般狀況下,會選擇用第三種方式編寫深度聚焦爬蟲。
# clone github scrapy-redis源碼文件 git clone https://github.com/rolando/scrapy-redis.git # 直接拿官方的項目範例,更名爲本身的項目用(針對懶癌患者) mv scrapy-redis/example-project ~/scrapy-youyuan
下面列舉了修改後的配置文件中與scrapy-redis有關的部分,middleware、proxy等內容在此就省略了。
# -*- coding: utf-8 -*- # 指定使用scrapy-redis的調度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 指定使用scrapy-redis的去重 DUPEFILTER_CLASS = 'scrapy_redis.dupefilters.RFPDupeFilter' # 指定排序爬取地址時使用的隊列, # 默認的 按優先級排序(Scrapy默認),由sorted set實現的一種非FIFO、LIFO方式。 SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderPriorityQueue' # 可選的 按先進先出排序(FIFO) # SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderQueue' # 可選的 按後進先出排序(LIFO) # SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.SpiderStack' # 在redis中保持scrapy-redis用到的各個隊列,從而容許暫停和暫停後恢復,也就是不清理redis queues SCHEDULER_PERSIST = True # 只在使用SpiderQueue或者SpiderStack是有效的參數,指定爬蟲關閉的最大間隔時間 # SCHEDULER_IDLE_BEFORE_CLOSE = 10 # 經過配置RedisPipeline將item寫入key爲 spider.name : items 的redis的list中,供後面的分佈式處理item # 這個已經由 scrapy-redis 實現,不須要咱們寫代碼 ITEM_PIPELINES = { 'example.pipelines.ExamplePipeline': 300, 'scrapy_redis.pipelines.RedisPipeline': 400 } # 指定redis數據庫的鏈接參數 # REDIS_PASS是我本身加上的redis鏈接密碼(默認不作) REDIS_HOST = '127.0.0.1' REDIS_PORT = 6379 #REDIS_PASS = 'redisP@ssw0rd' # LOG等級 LOG_LEVEL = 'DEBUG' #默認狀況下,RFPDupeFilter只記錄第一個重複請求。將DUPEFILTER_DEBUG設置爲True會記錄全部重複的請求。 DUPEFILTER_DEBUG =True # 覆蓋默認請求頭,能夠本身編寫Downloader Middlewares設置代理和UserAgent DEFAULT_REQUEST_HEADERS = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Connection': 'keep-alive', 'Accept-Encoding': 'gzip, deflate, sdch' }
# -*- coding: utf-8 -*- from datetime import datetime class ExamplePipeline(object): def process_item(self, item, spider): #utcnow() 是獲取UTC時間 item["crawled"] = datetime.utcnow() # 爬蟲名 item["spider"] = spider.name return item
增長咱們最後要保存的youyuanItem項,這裏只寫出來一個很是簡單的版本
# -*- coding: utf-8 -*- from scrapy.item import Item, Field class youyuanItem(Item): # 我的頭像連接 header_url = Field() # 用戶名 username = Field() # 心裏獨白 monologue = Field() # 相冊圖片連接 pic_urls = Field() # 年齡 age = Field() # 網站來源 youyuan source = Field() # 我的主頁源url source_url = Field() # 獲取UTC時間 crawled = Field() # 爬蟲名 spider = Field()
在spiders目錄下增長youyuan.py文件編寫咱們的爬蟲,以後就能夠運行爬蟲了。 這裏的提供一個簡單的版本:
# -*- coding:utf-8 -*- from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule # 使用redis去重 from scrapy.dupefilters import RFPDupeFilter from example.items import youyuanItem import re # class YouyuanSpider(CrawlSpider): name = 'youyuan' allowed_domains = ['youyuan.com'] # 有緣網的列表頁 start_urls = ['http://www.youyuan.com/find/beijing/mm18-25/advance-0-0-0-0-0-0-0/p1/'] # 搜索頁面匹配規則,根據response提取連接 list_page_lx = LinkExtractor(allow=(r'http://www.youyuan.com/find/.+')) # 北京、18~25歲、女性 的 搜索頁面匹配規則,根據response提取連接 page_lx = LinkExtractor(allow =(r'http://www.youyuan.com/find/beijing/mm18-25/advance-0-0-0-0-0-0-0/p\d+/')) # 我的主頁 匹配規則,根據response提取連接 profile_page_lx = LinkExtractor(allow=(r'http://www.youyuan.com/\d+-profile/')) rules = ( # 匹配find頁面,跟進連接,跳板 Rule(list_page_lx, follow=True), # 匹配列表頁成功,跟進連接,跳板 Rule(page_lx, follow=True), # 匹配我的主頁的連接,造成request保存到redis中等待調度,一旦有響應則調用parse_profile_page()回調函數處理,不作繼續跟進 Rule(profile_page_lx, callback='parse_profile_page', follow=False), ) # 處理我的主頁信息,獲得咱們要的數據 def parse_profile_page(self, response): item = youyuanItem() item['header_url'] = self.get_header_url(response) item['username'] = self.get_username(response) item['monologue'] = self.get_monologue(response) item['pic_urls'] = self.get_pic_urls(response) item['age'] = self.get_age(response) item['source'] = 'youyuan' item['source_url'] = response.url #print "Processed profile %s" % response.url yield item # 提取頭像地址 def get_header_url(self, response): header = response.xpath('//dl[@class=\'personal_cen\']/dt/img/@src').extract() if len(header) > 0: header_url = header[0] else: header_url = "" return header_url.strip() # 提取用戶名 def get_username(self, response): usernames = response.xpath("//dl[@class=\'personal_cen\']/dd/div/strong/text()").extract() if len(usernames) > 0: username = usernames[0] else: username = "NULL" return username.strip() # 提取心裏獨白 def get_monologue(self, response): monologues = response.xpath("//ul[@class=\'requre\']/li/p/text()").extract() if len(monologues) > 0: monologue = monologues[0] else: monologue = "NULL" return monologue.strip() # 提取相冊圖片地址 def get_pic_urls(self, response): pic_urls = [] data_url_full = response.xpath('//li[@class=\'smallPhoto\']/@data_url_full').extract() if len(data_url_full) <= 1: pic_urls.append(""); else: for pic_url in data_url_full: pic_urls.append(pic_url) if len(pic_urls) <= 1: return "NULL" # 每一個url用|分隔 return '|'.join(pic_urls) # 提取年齡 def get_age(self, response): age_urls = response.xpath("//dl[@class=\'personal_cen\']/dd/p[@class=\'local\']/text()").extract() if len(age_urls) > 0: age = age_urls[0] else: age = "0" age_words = re.split(' ', age) if len(age_words) <= 2: return "0" age = age_words[2][:-1] # 從age字符串開始匹配數字,失敗返回None if re.compile(r'[0-9]').match(age): return age return "0"
redis-server
scrapy crawl youyuan
在spiders目錄下增長youyuan.py文件編寫咱們的爬蟲,使其具備分佈式:
# -*- coding:utf-8 -*- from scrapy.linkextractors import LinkExtractor #from scrapy.spiders import CrawlSpider, Rule # 1. 導入RedisCrawlSpider類,不使用CrawlSpider from scrapy_redis.spiders import RedisCrawlSpider from scrapy.spiders import Rule from scrapy.dupefilters import RFPDupeFilter from example.items import youyuanItem import re # 2. 修改父類 RedisCrawlSpider # class YouyuanSpider(CrawlSpider): class YouyuanSpider(RedisCrawlSpider): name = 'youyuan' # 3. 取消 allowed_domains() 和 start_urls ##### allowed_domains = ['youyuan.com'] ##### start_urls = ['http://www.youyuan.com/find/beijing/mm18-25/advance-0-0-0-0-0-0-0/p1/'] # 4. 增長redis-key redis_key = 'youyuan:start_urls' list_page_lx = LinkExtractor(allow=(r'http://www.youyuan.com/find/.+')) page_lx = LinkExtractor(allow =(r'http://www.youyuan.com/find/beijing/mm18-25/advance-0-0-0-0-0-0-0/p\d+/')) profile_page_lx = LinkExtractor(allow=(r'http://www.youyuan.com/\d+-profile/')) rules = ( Rule(list_page_lx, follow=True), Rule(page_lx, follow=True), Rule(profile_page_lx, callback='parse_profile_page', follow=False), ) # 5. 增長__init__()方法,動態獲取allowed_domains() def __init__(self, *args, **kwargs): domain = kwargs.pop('domain', '') self.allowed_domains = filter(None, domain.split(',')) super(youyuanSpider, self).__init__(*args, **kwargs) # 處理我的主頁信息,獲得咱們要的數據 def parse_profile_page(self, response): item = youyuanItem() item['header_url'] = self.get_header_url(response) item['username'] = self.get_username(response) item['monologue'] = self.get_monologue(response) item['pic_urls'] = self.get_pic_urls(response) item['age'] = self.get_age(response) item['source'] = 'youyuan' item['source_url'] = response.url yield item # 提取頭像地址 def get_header_url(self, response): header = response.xpath('//dl[@class=\'personal_cen\']/dt/img/@src').extract() if len(header) > 0: header_url = header[0] else: header_url = "" return header_url.strip() # 提取用戶名 def get_username(self, response): usernames = response.xpath("//dl[@class=\'personal_cen\']/dd/div/strong/text()").extract() if len(usernames) > 0: username = usernames[0] else: username = "NULL" return username.strip() # 提取心裏獨白 def get_monologue(self, response): monologues = response.xpath("//ul[@class=\'requre\']/li/p/text()").extract() if len(monologues) > 0: monologue = monologues[0] else: monologue = "NULL" return monologue.strip() # 提取相冊圖片地址 def get_pic_urls(self, response): pic_urls = [] data_url_full = response.xpath('//li[@class=\'smallPhoto\']/@data_url_full').extract() if len(data_url_full) <= 1: pic_urls.append(""); else: for pic_url in data_url_full: pic_urls.append(pic_url) if len(pic_urls) <= 1: return "NULL" return '|'.join(pic_urls) # 提取年齡 def get_age(self, response): age_urls = response.xpath("//dl[@class=\'personal_cen\']/dd/p[@class=\'local\']/text()").extract() if len(age_urls) > 0: age = age_urls[0] else: age = "0" age_words = re.split(' ', age) if len(age_words) <= 2: return "0" age = age_words[2][:-1] if re.compile(r'[0-9]').match(age): return age return "0"
redis-server
scrapy runspider youyuan.py
redis-cli> lpush youyuan:start_urls http://www.youyuan.com/find/beijing/mm18-25/advance-0-0-0-0-0-0-0/p1/
有緣網的數據爬回來了,可是放在Redis裏沒有處理。以前咱們配置文件裏面沒有定製本身的ITEM_PIPELINES,而是使用了RedisPipeline,因此如今這些數據都被保存在redis的youyuan:items鍵中,因此咱們須要另外作處理。
在scrapy-youyuan目錄下能夠看到一個process_items.py
文件,這個文件就是scrapy-redis的example提供的從redis讀取item進行處理的模版。
假設咱們要把youyuan:items中保存的數據讀出來寫進MongoDB或者MySQL,那麼咱們能夠本身寫一個process_youyuan_profile.py
文件,而後保持後臺運行就能夠不停地將爬回來的數據入庫了。
啓動MongoDB數據庫:sudo mongod
執行下面程序:py2 process_youyuan_mongodb.py
# process_youyuan_mongodb.py # -*- coding: utf-8 -*- import json import redis import pymongo def main(): # 指定Redis數據庫信息 rediscli = redis.StrictRedis(host='192.168.199.108', port=6379, db=0) # 指定MongoDB數據庫信息 mongocli = pymongo.MongoClient(host='localhost', port=27017) # 建立數據庫名 db = mongocli['youyuan'] # 建立表名 sheet = db['beijing_18_25'] while True: # FIFO模式爲 blpop,LIFO模式爲 brpop,獲取鍵值 source, data = rediscli.blpop(["youyuan:items"]) item = json.loads(data) sheet.insert(item) try: print u"Processing: %(name)s <%(link)s>" % item except KeyError: print u"Error procesing: %r" % item if __name__ == '__main__':
main()
mysql.server start
(跟平臺不同)mysql -uroot -p
youyuan
:create database youyuan;
use youyuan
建立表beijing_18_25
以及全部字段的列名和數據類型。
py2 process_youyuan_mysql.py
#process_youyuan_mysql.py # -*- coding: utf-8 -*- import json import redis import MySQLdb def main(): # 指定redis數據庫信息 rediscli = redis.StrictRedis(host='192.168.199.108', port = 6379, db = 0) # 指定mysql數據庫 mysqlcli = MySQLdb.connect(host='127.0.0.1', user='power', passwd='xxxxxxx', db = 'youyuan', port=3306, use_unicode=True) while True: # FIFO模式爲 blpop,LIFO模式爲 brpop,獲取鍵值 source, data = rediscli.blpop(["youyuan:items"]) item = json.loads(data) try: # 使用cursor()方法獲取操做遊標 cur = mysqlcli.cursor() # 使用execute方法執行SQL INSERT語句 cur.execute("INSERT INTO beijing_18_25 (username, crawled, age, spider, header_url, source, pic_urls, monologue, source_url) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s )", [item['username'], item['crawled'], item['age'], item['spider'], item['header_url'], item['source'], item['pic_urls'], item['monologue'], item['source_url']]) # 提交sql事務 mysqlcli.commit() #關閉本次操做 cur.close() print "inserted %s" % item['source_url'] except MySQLdb.Error,e: print "Mysql Error %d: %s" % (e.args[0], e.args[1]) if __name__ == '__main__': main()
思考:如何將已有的Scrapy爬蟲項目,改寫成scrapy-redis分佈式爬蟲。
要求:將全部對應的大類的 標題和urls、小類的 標題和urls、子連接url、文章名以及文章內容,存入Redis數據庫。
# -*- coding: utf-8 -*- import scrapy import sys reload(sys) sys.setdefaultencoding("utf-8") class SinaItem(scrapy.Item): # 大類的標題 和 url parentTitle = scrapy.Field() parentUrls = scrapy.Field() # 小類的標題 和 子url subTitle = scrapy.Field() subUrls = scrapy.Field() # 小類目錄存儲路徑 subFilename = scrapy.Field() # 小類下的子連接 sonUrls = scrapy.Field() # 文章標題和內容 head = scrapy.Field() content = scrapy.Field()
# -*- coding: utf-8 -*- from scrapy import signals import sys reload(sys) sys.setdefaultencoding("utf-8") class SinaPipeline(object): def process_item(self, item, spider): sonUrls = item['sonUrls'] # 文件名爲子連接url中間部分,並將 / 替換爲 _,保存爲 .txt格式 filename = sonUrls[7:-6].replace('/','_') filename += ".txt" fp = open(item['subFilename']+'/'+filename, 'w') fp.write(item['content']) fp.close() return item
# -*- coding: utf-8 -*- BOT_NAME = 'Sina' SPIDER_MODULES = ['Sina.spiders'] NEWSPIDER_MODULE = 'Sina.spiders' ITEM_PIPELINES = { 'Sina.pipelines.SinaPipeline': 300, } LOG_LEVEL = 'DEBUG'
# -*- coding: utf-8 -*- from Sina.items import SinaItem import scrapy import os import sys reload(sys) sys.setdefaultencoding("utf-8") class SinaSpider(scrapy.Spider): name= "sina" allowed_domains= ["sina.com.cn"] start_urls= [ "http://news.sina.com.cn/guide/" ] def parse(self, response): items= [] # 全部大類的url 和 標題 parentUrls = response.xpath('//div[@id=\"tab01\"]/div/h3/a/@href').extract() parentTitle = response.xpath("//div[@id=\"tab01\"]/div/h3/a/text()").extract() # 全部小類的ur 和 標題 subUrls = response.xpath('//div[@id=\"tab01\"]/div/ul/li/a/@href').extract() subTitle = response.xpath('//div[@id=\"tab01\"]/div/ul/li/a/text()').extract() #爬取全部大類 for i in range(0, len(parentTitle)): # 指定大類目錄的路徑和目錄名 parentFilename = "./Data/" + parentTitle[i] #若是目錄不存在,則建立目錄 if(not os.path.exists(parentFilename)): os.makedirs(parentFilename) # 爬取全部小類 for j in range(0, len(subUrls)): item = SinaItem() # 保存大類的title和urls item['parentTitle'] = parentTitle[i] item['parentUrls'] = parentUrls[i] # 檢查小類的url是否以同類別大類url開頭,若是是返回True (sports.sina.com.cn 和 sports.sina.com.cn/nba) if_belong = subUrls[j].startswith(item['parentUrls']) # 若是屬於本大類,將存儲目錄放在本大類目錄下 if(if_belong): subFilename =parentFilename + '/'+ subTitle[j] # 若是目錄不存在,則建立目錄 if(not os.path.exists(subFilename)): os.makedirs(subFilename) # 存儲 小類url、title和filename字段數據 item['subUrls'] = subUrls[j] item['subTitle'] =subTitle[j] item['subFilename'] = subFilename items.append(item) #發送每一個小類url的Request請求,獲得Response連同包含meta數據 一同交給回調函數 second_parse 方法處理 for item in items: yield scrapy.Request( url = item['subUrls'], meta={'meta_1': item}, callback=self.second_parse) #對於返回的小類的url,再進行遞歸請求 def second_parse(self, response): # 提取每次Response的meta數據 meta_1= response.meta['meta_1'] # 取出小類裏全部子連接 sonUrls = response.xpath('//a/@href').extract() items= [] for i in range(0, len(sonUrls)): # 檢查每一個連接是否以大類url開頭、以.shtml結尾,若是是返回True if_belong = sonUrls[i].endswith('.shtml') and sonUrls[i].startswith(meta_1['parentUrls']) # 若是屬於本大類,獲取字段值放在同一個item下便於傳輸 if(if_belong): item = SinaItem() item['parentTitle'] =meta_1['parentTitle'] item['parentUrls'] =meta_1['parentUrls'] item['subUrls'] = meta_1['subUrls'] item['subTitle'] = meta_1['subTitle'] item['subFilename'] = meta_1['subFilename'] item['sonUrls'] = sonUrls[i] items.append(item) #發送每一個小類下子連接url的Request請求,獲得Response後連同包含meta數據 一同交給回調函數 detail_parse 方法處理 for item in items: yield scrapy.Request(url=item['sonUrls'], meta={'meta_2':item}, callback = self.detail_parse) # 數據解析方法,獲取文章標題和內容 def detail_parse(self, response): item = response.meta['meta_2'] content = "" head = response.xpath('//h1[@id=\"main_title\"]/text()') content_list = response.xpath('//div[@id=\"artibody\"]/p/text()').extract() # 將p標籤裏的文本內容合併到一塊兒 for content_one in content_list: content += content_one item['head']= head item['content']= content yield item
scrapy crawl sina
注:items數據直接存儲在Redis數據庫中,這個功能已經由scrapy-redis自行實現。除非單獨作額外處理(好比直接存入本地數據庫等),不然不用編寫pipelines.py代碼。
# items.py # -*- coding: utf-8 -*- import scrapy import sys reload(sys) sys.setdefaultencoding("utf-8") class SinaItem(scrapy.Item): # 大類的標題 和 url parentTitle = scrapy.Field() parentUrls = scrapy.Field() # 小類的標題 和 子url subTitle = scrapy.Field() subUrls = scrapy.Field() # 小類目錄存儲路徑 # subFilename = scrapy.Field() # 小類下的子連接 sonUrls = scrapy.Field() # 文章標題和內容 head = scrapy.Field() content = scrapy.Field()
# settings.py SPIDER_MODULES = ['Sina.spiders'] NEWSPIDER_MODULE = 'Sina.spiders' USER_AGENT = 'scrapy-redis (+https://github.com/rolando/scrapy-redis)' DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" SCHEDULER = "scrapy_redis.scheduler.Scheduler" SCHEDULER_PERSIST = True SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue" #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue" #SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack" ITEM_PIPELINES = { # 'Sina.pipelines.SinaPipeline': 300, 'scrapy_redis.pipelines.RedisPipeline': 400, } LOG_LEVEL = 'DEBUG' # Introduce an artifical delay to make use of parallelism. to speed up the # crawl. DOWNLOAD_DELAY = 1 REDIS_HOST = "192.168.13.26" REDIS_PORT = 6379
# sina.py # -*- coding: utf-8 -*- from Sina.items import SinaItem from scrapy_redis.spiders import RedisSpider #from scrapy.spiders import Spider import scrapy import sys reload(sys) sys.setdefaultencoding("utf-8") #class SinaSpider(Spider): class SinaSpider(RedisSpider): name= "sina" redis_key = "sinaspider:start_urls" #allowed_domains= ["sina.com.cn"] #start_urls= [ # "http://news.sina.com.cn/guide/" #]#起始urls列表 def __init__(self, *args, **kwargs): domain = kwargs.pop('domain', '') self.allowed_domains = filter(None, domain.split(',')) super(SinaSpider, self).__init__(*args, **kwargs) def parse(self, response): items= [] # 全部大類的url 和 標題 parentUrls = response.xpath('//div[@id=\"tab01\"]/div/h3/a/@href').extract() parentTitle = response.xpath("//div[@id=\"tab01\"]/div/h3/a/text()").extract() # 全部小類的ur 和 標題 subUrls = response.xpath('//div[@id=\"tab01\"]/div/ul/li/a/@href').extract() subTitle = response.xpath('//div[@id=\"tab01\"]/div/ul/li/a/text()').extract() #爬取全部大類 for i in range(0, len(parentTitle)): # 指定大類的路徑和目錄名 #parentFilename = "./Data/" + parentTitle[i] #若是目錄不存在,則建立目錄 #if(not os.path.exists(parentFilename)): # os.makedirs(parentFilename) # 爬取全部小類 for j in range(0, len(subUrls)): item = SinaItem() # 保存大類的title和urls item['parentTitle'] = parentTitle[i] item['parentUrls'] = parentUrls[i] # 檢查小類的url是否以同類別大類url開頭,若是是返回True (sports.sina.com.cn 和 sports.sina.com.cn/nba) if_belong = subUrls[j].startswith(item['parentUrls']) # 若是屬於本大類,將存儲目錄放在本大類目錄下 if(if_belong): #subFilename =parentFilename + '/'+ subTitle[j] # 若是目錄不存在,則建立目錄 #if(not os.path.exists(subFilename)): # os.makedirs(subFilename) # 存儲 小類url、title和filename字段數據 item['subUrls'] = subUrls[j] item['subTitle'] =subTitle[j] #item['subFilename'] = subFilename items.append(item) #發送每一個小類url的Request請求,獲得Response連同包含meta數據 一同交給回調函數 second_parse 方法處理 for item in items: yield scrapy.Request( url = item['subUrls'], meta={'meta_1': item}, callback=self.second_parse) #對於返回的小類的url,再進行遞歸請求 def second_parse(self, response): # 提取每次Response的meta數據 meta_1= response.meta['meta_1'] # 取出小類裏全部子連接 sonUrls = response.xpath('//a/@href').extract() items= [] for i in range(0, len(sonUrls)): # 檢查每一個連接是否以大類url開頭、以.shtml結尾,若是是返回True if_belong = sonUrls[i].endswith('.shtml') and sonUrls[i].startswith(meta_1['parentUrls']) # 若是屬於本大類,獲取字段值放在同一個item下便於傳輸 if(if_belong): item = SinaItem() item['parentTitle'] =meta_1['parentTitle'] item['parentUrls'] =meta_1['parentUrls'] item['subUrls'] =meta_1['subUrls'] item['subTitle'] =meta_1['subTitle'] #item['subFilename'] = meta_1['subFilename'] item['sonUrls'] = sonUrls[i] items.append(item) #發送每一個小類下子連接url的Request請求,獲得Response後連同包含meta數據 一同交給回調函數 detail_parse 方法處理 for item in items: yield scrapy.Request(url=item['sonUrls'], meta={'meta_2':item}, callback = self.detail_parse) # 數據解析方法,獲取文章標題和內容 def detail_parse(self, response): item = response.meta['meta_2'] content = "" head = response.xpath('//h1[@id=\"main_title\"]/text()').extract() content_list = response.xpath('//div[@id=\"artibody\"]/p/text()').extract() # 將p標籤裏的文本內容合併到一塊兒 for content_one in content_list: content += content_one item['head']= head[0] if len(head) > 0 else "NULL" item['content']= content yield item
slave端:
scrapy runspider sina.py
Master端:
redis-cli> lpush sinaspider:start_urls http://news.sina.com.cn/guide/
IT桔子是關注IT互聯網行業的結構化的公司數據庫和商業信息服務提供商,於2013年5月21日上線。
IT桔子致力於經過信息和數據的生產、聚合、挖掘、加工、處理,幫助目標用戶和客戶節約時間和金錢、提升效率,以輔助其各種商業行爲,包括風險投資、收購、競爭情報、細分行業信息、國外公司產品信息數據服務等。
用於需自行對所發表或採集的內容負責,因所發表或採集的內容引起的一切糾紛、損失,由該內容的發表或採集者承擔所有直接或間接(連帶)法律責任,IT桔子不承擔任何法律責任。
項目採集地址:http://www.itjuzi.com/company
要求:採集頁面下全部創業公司的公司信息,包括如下但不限於:
# items.py # -*- coding: utf-8 -*- import scrapy class CompanyItem(scrapy.Item): # 公司id (url數字部分) info_id = scrapy.Field() # 公司名稱 company_name = scrapy.Field() # 公司口號 slogan = scrapy.Field() # 分類 scope = scrapy.Field() # 子分類 sub_scope = scrapy.Field() # 所在城市 city = scrapy.Field() # 所在區域 area = scrapy.Field() # 公司主頁 home_page = scrapy.Field() # 公司標籤 tags = scrapy.Field() # 公司簡介 company_intro = scrapy.Field() # 公司全稱: company_full_name = scrapy.Field() # 成立時間: found_time = scrapy.Field() # 公司規模: company_size = scrapy.Field() # 運營狀態 company_status = scrapy.Field() # 投資狀況列表:包含獲投時間、融資階段、融資金額、投資公司 tz_info = scrapy.Field() # 團隊信息列表:包含成員姓名、成員職稱、成員介紹 tm_info = scrapy.Field() # 產品信息列表:包含產品名稱、產品類型、產品介紹 pdt_info = scrapy.Field()
# items.py # -*- coding: utf-8 -*- import scrapy class CompanyItem(scrapy.Item): # 公司id (url數字部分) info_id = scrapy.Field() # 公司名稱 company_name = scrapy.Field() # 公司口號 slogan = scrapy.Field() # 分類 scope = scrapy.Field() # 子分類 sub_scope = scrapy.Field() # 所在城市 city = scrapy.Field() # 所在區域 area = scrapy.Field() # 公司主頁 home_page = scrapy.Field() # 公司標籤 tags = scrapy.Field() # 公司簡介 company_intro = scrapy.Field() # 公司全稱: company_full_name = scrapy.Field() # 成立時間: found_time = scrapy.Field() # 公司規模: company_size = scrapy.Field() # 運營狀態 company_status = scrapy.Field() # 投資狀況列表:包含獲投時間、融資階段、融資金額、投資公司 tz_info = scrapy.Field() # 團隊信息列表:包含成員姓名、成員職稱、成員介紹 tm_info = scrapy.Field() # 產品信息列表:包含產品名稱、產品類型、產品介紹 pdt_info = scrapy.Field()
# -*- coding: utf-8 -*- BOT_NAME = 'itjuzi' SPIDER_MODULES = ['itjuzi.spiders'] NEWSPIDER_MODULE = 'itjuzi.spiders' # Enables scheduling storing requests queue in redis. SCHEDULER = "scrapy_redis.scheduler.Scheduler" # Ensure all spiders share same duplicates filter through redis. DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # REDIS_START_URLS_AS_SET = True COOKIES_ENABLED = False DOWNLOAD_DELAY = 1.5 # 支持隨機下載延遲 RANDOMIZE_DOWNLOAD_DELAY = True # Obey robots.txt rules ROBOTSTXT_OBEY = False ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 300 } DOWNLOADER_MIDDLEWARES = { # 該中間件將會收集失敗的頁面,並在爬蟲完成後從新調度。(失敗狀況可能因爲臨時的問題,例如鏈接超時或者HTTP 500錯誤致使失敗的頁面) 'scrapy.downloadermiddlewares.retry.RetryMiddleware': 80, # 該中間件提供了對request設置HTTP代理的支持。您能夠經過在 Request 對象中設置 proxy 元數據來開啓代理。 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 100, 'itjuzi.middlewares.RotateUserAgentMiddleware': 200, } REDIS_HOST = "192.168.199.108" REDIS_PORT = 6379
middlewares.py # -*- coding: utf-8 -*- from scrapy.contrib.downloadermiddleware.useragent import UserAgentMiddleware import random # User-Agetn 下載中間件 class RotateUserAgentMiddleware(UserAgentMiddleware): def __init__(self, user_agent=''): self.user_agent = user_agent def process_request(self, request, spider): # 這句話用於隨機選擇user-agent ua = random.choice(self.user_agent_list) request.headers.setdefault('User-Agent', ua) user_agent_list = [ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1", "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5", "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10", "Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/533.17.8 (KHTML, like Gecko) Version/5.0.1 Safari/533.17.8", "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5", "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.1.17) Gecko/20110123 (like Firefox/3.x) SeaMonkey/2.0.12", "Mozilla/5.0 (Windows NT 5.2; rv:10.0.1) Gecko/20100101 Firefox/10.0.1 SeaMonkey/2.7.1", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/532.8 (KHTML, like Gecko) Chrome/4.0.302.2 Safari/532.8", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.464.0 Safari/534.3", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.15 Safari/534.13", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.186 Safari/535.1", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.54 Safari/535.2", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.36 Safari/535.7", "Mozilla/5.0 (Macintosh; U; Mac OS X Mach-O; en-US; rv:2.0a) Gecko/20040614 Firefox/3.0.0 ", "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.0.3) Gecko/2008092414 Firefox/3.0.3", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1) Gecko/20090624 Firefox/3.5", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.14) Gecko/20110218 AlexaToolbar/alxf-2.0 Firefox/3.6.14", "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1" ]
# -*- coding: utf-8 -*- from bs4 import BeautifulSoup from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from scrapy_redis.spiders import RedisCrawlSpider from itjuzi.items import CompanyItem class ITjuziSpider(RedisCrawlSpider): name = 'itjuzi' allowed_domains = ['www.itjuzi.com'] # start_urls = ['http://www.itjuzi.com/company'] redis_key = 'itjuzispider:start_urls' rules = [ # 獲取每一頁的連接 Rule(link_extractor=LinkExtractor(allow=('/company\?page=\d+'))), # 獲取每個公司的詳情 Rule(link_extractor=LinkExtractor(allow=('/company/\d+')), callback='parse_item') ] def parse_item(self, response): soup = BeautifulSoup(response.body, 'lxml') # 開頭部分: //div[@class="infoheadrow-v2 ugc-block-item"] cpy1 = soup.find('div', class_='infoheadrow-v2') if cpy1: # 公司名稱://span[@class="title"]/b/text()[1] company_name = cpy1.find(class_='title').b.contents[0].strip().replace('\t', '').replace('\n', '') # 口號: //div[@class="info-line"]/p slogan = cpy1.find(class_='info-line').p.get_text() # 分類:子分類//span[@class="scope c-gray-aset"]/a[1] scope_a = cpy1.find(class_='scope c-gray-aset').find_all('a') # 分類://span[@class="scope c-gray-aset"]/a[1] scope = scope_a[0].get_text().strip() if len(scope_a) > 0 else '' # 子分類:# //span[@class="scope c-gray-aset"]/a[2] sub_scope = scope_a[1].get_text().strip() if len(scope_a) > 1 else '' # 城市+區域://span[@class="loca c-gray-aset"]/a city_a = cpy1.find(class_='loca c-gray-aset').find_all('a') # 城市://span[@class="loca c-gray-aset"]/a[1] city = city_a[0].get_text().strip() if len(city_a) > 0 else '' # 區域://span[@class="loca c-gray-aset"]/a[2] area = city_a[1].get_text().strip() if len(city_a) > 1 else '' # 主頁://a[@class="weblink marl10"]/@href home_page = cpy1.find(class_='weblink marl10')['href'] # 標籤://div[@class="tagset dbi c-gray-aset"]/a tags = cpy1.find(class_='tagset dbi c-gray-aset').get_text().strip().strip().replace('\n', ',') #基本信息://div[@class="block-inc-info on-edit-hide"] cpy2 = soup.find('div', class_='block-inc-info on-edit-hide') if cpy2: # 公司簡介://div[@class="block-inc-info on-edit-hide"]//div[@class="des"] company_intro = cpy2.find(class_='des').get_text().strip() # 公司全稱:成立時間:公司規模:運行狀態://div[@class="des-more"] cpy2_content = cpy2.find(class_='des-more').contents # 公司全稱://div[@class="des-more"]/div[1] company_full_name = cpy2_content[1].get_text().strip()[len('公司全稱:'):] if cpy2_content[1] else '' # 成立時間://div[@class="des-more"]/div[2]/span[1] found_time = cpy2_content[3].contents[1].get_text().strip()[len('成立時間:'):] if cpy2_content[3] else '' # 公司規模://div[@class="des-more"]/div[2]/span[2] company_size = cpy2_content[3].contents[3].get_text().strip()[len('公司規模:'):] if cpy2_content[3] else '' #運營狀態://div[@class="des-more"]/div[3] company_status = cpy2_content[5].get_text().strip() if cpy2_content[5] else '' # 主體信息: main = soup.find('div', class_='main') # 投資狀況://table[@class="list-round-v2 need2login"] # 投資狀況,包含獲投時間、融資階段、融資金額、投資公司 tz = main.find('table', 'list-round-v2') tz_list = [] if tz: all_tr = tz.find_all('tr') for tr in all_tr: tz_dict = {} all_td = tr.find_all('td') tz_dict['tz_time'] = all_td[0].span.get_text().strip() tz_dict['tz_round'] = all_td[1].get_text().strip() tz_dict['tz_finades'] = all_td[2].get_text().strip() tz_dict['tz_capital'] = all_td[3].get_text().strip().replace('\n', ',') tz_list.append(tz_dict) # 團隊信息:成員姓名、成員職稱、成員介紹 tm = main.find('ul', class_='list-prodcase limited-itemnum') tm_list = [] if tm: for li in tm.find_all('li'): tm_dict = {} tm_dict['tm_m_name'] = li.find('span', class_='c').get_text().strip() tm_dict['tm_m_title'] = li.find('span', class_='c-gray').get_text().strip() tm_dict['tm_m_intro'] = li.find('p', class_='mart10 person-des').get_text().strip() tm_list.append(tm_dict) # 產品信息:產品名稱、產品類型、產品介紹 pdt = main.find('ul', class_='list-prod limited-itemnum') pdt_list = [] if pdt: for li in pdt.find_all('li'): pdt_dict = {} pdt_dict['pdt_name'] = li.find('h4').b.get_text().strip() pdt_dict['pdt_type'] = li.find('span', class_='tag yellow').get_text().strip() pdt_dict['pdt_intro'] = li.find(class_='on-edit-hide').p.get_text().strip() pdt_list.append(pdt_dict) item = CompanyItem() item['info_id'] = response.url.split('/')[-1:][0] item['company_name'] = company_name item['slogan'] = slogan item['scope'] = scope item['sub_scope'] = sub_scope item['city'] = city item['area'] = area item['home_page'] = home_page item['tags'] = tags item['company_intro'] = company_intro item['company_full_name'] = company_full_name item['found_time'] = found_time item['company_size'] = company_size item['company_status'] = company_status item['tz_info'] = tz_list item['tm_info'] = tm_list item['pdt_info'] = pdt_list return item
# Automatically created by: scrapy startproject # # For more information about the [deploy] section see: # https://scrapyd.readthedocs.org/en/latest/deploy.html [settings] default = itjuzi.settings [deploy] #url = http://localhost:6800/ project = itjuzi
Slave端:
scrapy runspider juzi.py
Master端:
redis-cli > lpush itjuzispider:start_urls http://www.itjuzi.com/company