网页爬虫--scrapy进阶
2017-10-14 21:51
253 查看
原文地址
1. 站点选取
现在的大网站基本除了pc端都会有移动端,所以需要先确定爬哪个。比如爬新浪微博,有以下几个选择:
www.weibo.com,主站
www.weibo.cn,简化版
m.weibo.cn,移动版
上面三个中,主站的微博数据是动态加载的,意味着光看源码是看不到数据的,想爬的话要么搞清楚其api访问情况,要么模拟js,那样的话花的力气可能就有点多了。weibo.cn是一个简化版,数据能直接从网页源代码中解析出来,但使用正则或xpath取网页中的元素仍然是无聊且费时的,更不用说偶尔的页面结构错误更让人抓狂。 相比之下,移动版的爬虫最好写,因为移动版能直接拿到json格式的数据。
一般来说,有移动版的网站优先爬移动版,会节省很多力气。
2. 模拟登录
现在需要登录才能正常浏览的网站的越来越多了,对爬虫也越来越不友好...所以模拟登录在不少场合都是必不可少的。首先,最简单的模拟登录是只有用户名密码的登录。这时候只需要在发送第一个请求时加上登录表单数据即可:
def start_requests(self): return scrapy.FormRequest( formdata={'username': '***', 'password': '***'}, callback=self.after_login )
如果不知道登录页面是哪一个的话,也可以在返回第一个请求后登录:
def parse(self, response): return scrapy.FormRequest.from_response( response, formdata={'username': '***', 'password': '***'}, callback=self.after_login )
为了保持登录,注意cookie是不能关闭的(默认情况是开着的,可以在settings.py中设置)。
如果需要验证码的话,网上有一些提取分析验证码图片的包,可以提取出来然后手动输入验证码。
上面只是一些简单的登录情况,如果验证码很变态(比如需要鼠标滑动)或者登录过程很复杂,需要各种加密(比如新浪微博pc端的登陆)的话,模拟登录确实是个很让人头大的问题。这时候有另一个通用办法,那就是cookie模拟登录。网站想知道我们的登录状态,都是通过cookie来确认的,所以我们只需要在每次request的时候都附带上cookie即可实现已登录的效果。
那么,如何获得cookie呢?有chrome的可以F12打开Network界面,这时候人工网页登录,即可在headers中看到cookie。得到cookie后,只需要在request中加入自己的cookie即可。
self.cookie = {"_T_WM": self.cookie_T_WM, ... "SSOLoginState": self.cookie_SSOLoginState} return Request(url, cookies=self.cookie, callback=self.parse_page)
3. 网页解析
一般来说,使用xpath和css已经可以应付所有的html源码了,剩下的只是耐心加细心...要是有心的话也可以Item Loaders,方便后期的维护,下面摘自官方文档:def parse(self, response): l = ItemLoader(item=Product(), response=response) l.add_xpath('name', '//div[@class="product_name"]') l.add_xpath('name', '//div[@class="product_title"]') l.add_xpath('price', '//p[@id="price"]') l.add_css('stock', 'p#stock]') l.add_value('last_updated', 'today') return l.load_item()
值得一提的是如果获取的是json格式的数据,可以使用python自带的json库来解析成一个字典或列表:
data = json.loads(response.body)
4. 数据存储
可以使用twisted提供的数据库库来维护一个连接池:class CnblogPipeline(object): def __init__(self): self.dbpool = adbapi.ConnectionPool('MySQLdb', host='localhost', db='cnblog', user='root', passwd='root', cursorclass=MySQLdb.cursors.DictCursor, charset='utf8', use_unicode=True) def process_item(self, item, spider): self.dbpool.runInteraction(self.cnblog_insert, item) return item def cnblog_insert(self, cur, item): try: cur.execute('insert into ***') exception MySQLdb.Error, e: logging.info("cnblog_insert:%s" % str(e))
如果爬的是社交网站这种有着树型结构关系的网站的话,mongodb其符合人的思维的存储方式让其成为首选。
如果使用mysql的话记得将innodb_flush_log_at_trx_commit这个参数置为0(每一秒读写一次数据和log),可以大大提高读写速度。
5. scrapy小知识点
Request传递消息。在Request中加入meta,即可将meta传递给response。Request(url, meta={'how': 'ok'}, callback=self.parse_page) def parse_page(self, response): print response.meta['how']
CrawlSpider。都知道在写自己的spider的时候需要继承scrapy的spider,除了scrapy.Spider外,scrapy还提供了好几种spider,其中CrawlSpider算是比较常用的。CrawlSpider的优势在于可以用rules方便地规定新的url的样子,即通过正则匹配来约束url。并且不需要自己生成新的url,CrawlSpider会自己寻找源码中所有符合要求的新url的。另外,rules的回调方法名字最好不要叫parse。
class CnblogSpider(CrawlSpider): name = "cnblog_spider" allowed_domain = ["cnblog.com"] start_urls = ["http://www.cnblogs.com/rubinorth"] rules = [ Rule(LinkExtractor(allow=r"http://www.cnblogs.com/rubinorth/p/\d+\.html"), callback="parse_page", follow=True) ]
parse中既返回item又生成新的request。平时在parse中return item即可返回item,return request则生成新的request请求。如果我们将return换为yield的话即可既返回item又生成新的request。注意一旦使用了yield,那么parse方法中就不能有return了。
def parse_page(self, response): item = CnblogItem() **** yield item yield Request(new_url, callback=self.parse_page)
每个spider不同设置。在spider中加入custom_settings即可覆盖settings.py中相应的设置,这样的话在settings.py中只需要放一些公用的设置就行了。最常用的就是设置每个spider的pipeline。
custom_settings={ 'ITEM_PIPELINES' : { 'cnblog_project.pipelines.CnblogPipeline': 300, } }
读取自己的设置。不管在spider中还是pipeline中,都可以写from_crawler这个方法(注意spider中参数数目不同)。此方法在初始化阶段由scrapy自己调用,其最大的作用就是从settings.py读取自己的设置了。下面的代码从settings.py中读取了MONGO_URI。
class MongoPipeline(object): def __init__(self, mongo_uri, mongo_db): self.mongo_uri = mongo_uri @classmethod def from_crawler(cls, crawler): return cls( mongo_uri=crawler.settings.get('MONGO_URI') )
顺序。settings.py中middleware和pipeline设置的时候需要在后面跟上一个数字,而这个数字的含义就是调用的顺序,数字越小越早调用。
DOWNLOADER_MIDDLEWARES = { 'cnblog_project.my_mv.middleware.UserAgentMiddleware': 543, 'cnblog_project.my_mv.middleware.ProxyMiddleware':544, }
pipeline中spider.name的应用。pipeline中的process_item中可以根据spider.name来对不同的item进行不同的处理。
def process_item(self, item, spider): if spider.name == 'a': **** if spider.name == 'b': ****
多个pipeline处理一个item。pipeline中的process_item方法必须返回一个item或者raise一个DropItem的异常,如果返回item的话这个item将会被之后的pipeline接收到。
def process_item(self, item, spider): return item
之后可能会有添加。
其实这些在scrapy的官方文档都有提及,在此只是总结一些常用的知识点。若想更深入地了解scrapy,定然是阅读其官方文档最好了。:)
6. scrapy_redis简介
scrapy_redis是一个分布式爬虫的解决方案。其思想为多个爬虫共用一个爬取队列,此队列使用redis存储,因为redis是一个内存数据库,所以速度上与单机的队列相差不大。那么,scrapy_redis到底对scrapy做了什么修改,达到分布式的目的呢?
查看github上面的源码,可以发现其功能性代码集中在scheduler.py,dupefilter.py和queue.py中,分别是调度器(部分功能),去重,以及redis队列的实现。scrapy_redis就是将这些功能代替了scrapy原本的功能(并没有修改scrapy源码,只需要在settings.py中进行设置即可),从而达到了分布式的效果。
相关文章推荐
- 网页爬虫--scrapy进阶
- 网页爬虫--scrapy进阶
- 网页爬虫--scrapy进阶
- Python爬虫教程——进阶一之爬虫框架Scrapy安装配置
- Scrapy爬虫框架教程(四)-- 抓取AJAX异步加载网页
- Python爬虫进阶一之爬虫框架Scrapy安装配置
- 通过scrapy shell url利用xpath解析网页(为避免反爬虫封掉IP)
- Scrapy + Mongo 构建一个网页爬虫
- 网页爬虫--scrapy入门
- Python爬虫系列之----Scrapy(五)网页提取的三种方式(正则,Beautiful Soup,Lxml)
- scrapy爬虫获取网页特定内容
- 静觅 » Python爬虫进阶三之Scrapy框架安装配置
- Scrapy定向爬虫教程(二)——提取网页内容
- 芝麻HTTP:Python爬虫进阶之Scrapy框架安装配置
- 关于scrapy新闻爬虫,对新闻网页内容进行编辑的问题
- Scrapy:Python实现scrapy框架爬虫两个网址下载网页内容信息——Jason niu
- 网络爬虫之Scrapy实战三:爬取多个网页CrawlSpider
- Scrapy 爬虫框架爬取网页数据
- python爬虫框架scrapy实战之爬取京东商城进阶篇
- Python3+Scrapy实现网页爬虫