python爬⾍设计体会_最近所学——爬⾍⼼得以及学习体会
(本⼈的第⼀篇博客)...
由于论⽂的关系,要⼤量的微博⽂本数据,在⽹上查了很多,没有可以直接⽤的现成数据,因此就⼊了爬⾍的坑,通过同学介绍看了《精通Python⽹络爬⾍》的书,也结合⼀些⼤⽜的博客,如愿获得了⾃⼰想要的数据。在这主要记录⼀下⾃⼰学习这本书的⼼得、⾃⼰爬取微博数据的过程以及中途遇到的⼀些问题。
关于精通Python⽹络爬⾍这本书,对于⼀个从来没解除过爬⾍的我来说,这可以算是⼀个⾮常好⼊门的书籍了,只要稍微有点Python的基础知识就可以很好学习这本书了。通过学习这本书,发现其实对于爬取web数据来说,最重要的就是如何获取⼤量的数据以及对于想要的数据的提取了,这⾥就不得不说到对于⽹页地址的分析以及正则表达式了,以及如何模拟登陆还有关于爬⾍的Scrapy框架的知识。这本书的⼀些基础内容就不说了(包括爬⾍的原理以及Python应⽤于爬⾍的⽐如Urllib这样的库等等),直接进⼊我想要说的⼀些爬⾍的重点,这些不算是难点,爬⾍整体来说还是⽐较简单的。
⾸先,说⼀下我对正则表达式的理解,通过python中的⼀些函数,可以将⽹页的源代码获取下来,但是如何从这些源代码中得到⾃⼰想要的数据呢(⽐如图⽚或者是⼀些⽂章标题以及微博内容),这就⽤到了正则表达式了,可能对于很多⼈来说正则表达式并不是个问题,因为正则表达式只要稍微⽤⼏次就可以
很好的掌握了,但是对于刚开始的我,对于正则表达式确实是有点烦的,起初就是觉得有点⿇烦,也不知道怎么写出正好可以匹配的正则来提取出想要的内容,通过学习之后,发现这是⼀个很灵活的东西,只要可以到唯⼀识别的标识或字符串,就可以很容易的到⾃⼰想要的东西,⽽且正则的编写也没有⼀个固定的格式,只要能匹配到⾃⼰想要的东西就是可以的。关于正则表达式的基础知识,我主要想说⼏个⽤的⽐较多也⽐较好⽤的:(1)“.”是⽤来匹配除换⾏符以外的任意字符;(2)“*”是⽤来匹配0次、1次或多次前⾯的原⼦;(3)“?”是⽤来匹配0次或1次前⾯的原⼦;(4)第4个就要说前⾯三个的组合了,也就是“.*?”了,这个就是懒惰模式了,不加问好就是贪婪模式,它们两个的区别是,懒惰模式采⽤的是就近匹配原则,可以让匹配结果更加的精确,⼆贪婪模式就是尽可能多地匹配。关于正则,我就不举例⼦了,只要能唯⼀匹配到⾃⼰想要的字符串,那就是好的正则表达式,不过要注意的是,正则表达式中有时候会加⼊“\”转义字符;还有⼀个要注意的是,在正则表达式中(),两个圆括号就代表了最终匹配的结果是圆括号⾥的内容,⽽不是整个正则表达式所匹配的内容,除⾮两个圆括号前⾯都有转义字符。除了正则表达式,还有⼀些获取⽹页源代码中⾃⼰想要的标签的⽅法,就是xpath 表达式等。⽬前我也只会使⽤正则和xpath。
⽽加载⼀次的⽹址是
其中的102803_ctg1_1388_-_ctg1_1388代表的是⼀个类别,这⾥代表的是体育类,因此这个不重要,对于不同的类,只要将
102803_ctg1_1388_-_ctg1_1388⾥后⾯两个数字替换⼀下即可。观察这两个⽹址,发现⾥⾯最主要的是四个参数,也就是pagebar、current_page、pre_page、page,其他的对页⾯都不会有什么影响,就不⽤管它们。对于这四个参数,再观察可以发现只有pagebar和current_page发⽣了变化,我刚开始分析,只加载了三四页,就得出结论微博数据的页⾯翻页由pagebar和current_page来控制的,可是当我爬取数据的时候,随着pagebar和current_page越来越⼤,发现页⾯获取的内容就是空的,说明是页⾯翻页的分析是不对的。之后我⼜通过Fiddler分析了24次加载的过程,发现在微博页⾯⾥,六页⼀个周期,没加载够六次,需要⼿动点击显⽰更多来显⽰更多的微博内容,分析了24次的⽹页地址结果我就不放上来了,有兴趣的可以通过抓包软件⾃⼰去看看,我直接把分析的结果放上来。加载24页后,我发现了页⾯的翻页是这四个参数来控制,⽽且还是有规律的,current_page是从1开始,没加载⼀页,就加1;⽽pagebar是从0开始,加载到4,0-4也就是5个页⾯,第6个页⾯pagebar就不在⽹页地址中了,然后下⼀次加载pagebar就继续从0开始,是⼀个循环,6是周期;然后就是pre_page,这个我理解的是当前⼤页码的意思,它的值,起初的六个页⾯pre_page都是1,第⼆次的六个页⾯它的值都是2,因此我之前分析了三四次的加载都看不出它的变化也是这个原因;page和pre_page类似,对于起初的六个页⾯中的前五个页⾯page是1,第六个页⾯page变成了2,杜宇第⼆次的六个页⾯中的前五个页⾯page是2,第六个页⾯就变成了3;通过这些分析,我把微博某⼀类别微博数据的页⾯翻页理解为由这四个参数来控制,⽽且还是⼀个⼆层页⾯的样⼦,由pre_page和page来控制⼤页⾯页码,由pagebar来控制⼩页页码,current_page来控制⼩页的数量,⽤这个分析继续去爬取数据,发现随着值
的增⼤,获取的数据⼜编程了空,发现⼜分析错了。这次我⼜将页⾯的加载次数增加到30次,这时候四个参数的变化都和之前的分析⼀样,只是加载到第30页之后,在⽹站⾥就没有“显⽰更多”的字眼了,这时候不能加载更多的微博数据了,这时就有点不知道该怎么继续去分析页⾯翻页的控制参数了,然后我就打开了此时⽹页的源代码来查看,发现了,微博显⽰的这⼀⼤栏⾥,源代码⾥有个pagenum=1的字眼,这时我推断这个参数可能是控制参数,这样⼦的话,微博的页⾯翻页就是⼀个三层翻页过程了,有pagenum控制最⼤的页⾯,这个页⾯⼜分为5个中页,30个⼩页,也就是current_page 从1加载到30,然后pagenum就加1了。这次利⽤这个分析构造出来的微博数据⽹页地址来获取微博数据,发现不会出现空页⾯了,因此这个分析就是正确的了。说了这么多关于页⾯翻页分析的东西,其实这个页⾯分析并没有那么难,只是是⼀个需要⾃⼰去细细观察和要有耐⼼的⼯作,针对不同的⽹站,只要分析出⽹页地址翻页或者关键词的控制参数,就可以⾃动的爬取数据了。对于关键词的参数控制,也是可以显⽰在⽹页地址中的,就⽐如刚才102803_ctg1_1388_-_ctg1_1388代表体育,102803_ctg1_2088_-_ctg1_2088就代表科技了,只要通过观察⽹页地址的变化以及⼀些经验就可以轻松的得出了。
from selenium importwebdriver
browser=webdriver.PhantomJS(executable_path="D:/phantomjs-2.1.1-windows/") #加载某个浏览器引擎
<("login.sina/sso/login.phpclient=ssologin.js(v1.4.19)") # 请求微博真实登录地址
time.sleep(3)
browser.find_element_by_xpath('//input[@name="username"]').send_keys("你的⽤户名")
browser.find_element_by_xpath('//input[@name="password"]').send_keys("你的密码")
browser.find_element_by_xpath('//input[@class="W_btn_a btn_34px"]').click()
time.sleep(3)
其中的find_element_by_xpath⽅法是通过xpath表达式来到真实登录⽹页源代码中输⼊⽤户名和密码以及点击登录的标签的。通过这简单的⼏⾏代码就可以完成微博的模拟登陆了。python正则表达式不包含
最后就是要说下scrapy框架了,scrapy框架是⼀个很简便的爬⾍框架,⾥⾯可以很⽅便的设置⼀些避免爬⾍被禁⽌的操作,⽐如禁⽌cookie,使⽤ip池⽤户代理池等,这就是settings.py。还可以对⾃⼰想要获取的数据设置⼀个存储容器,这就是scrapy框架的items.py。还可以对获取的数据进⾏后续的处理,⽐如存⼊数据库之类的,这就是pipelines.py。还有⼀个中间件⽂件middlewares.py,这个⽂件和
scrapy中最重要的爬⾍⽂件是最重要的两个⽂件,对于中间件⽂件中,我们可以设置⼀些类,然后在settings⽂件中启动这个类,这样,对于爬⾍⽂件中每次发送的Request都会经过中间件,这样可以利⽤中间件去做⼀些操作(具体视情况⽽定,这个我也不是很懂,只是按照⾃⼰的理解来说的,还是需要积累更多经验才能彻底了解),⽽对于scrapy中的爬⾍⽂件,这是scrapy中最重要的⽂件了,我在这⾥主要说下爬⾍的运⾏流程,直接贴图⽚来展⽰,scrapy中的爬⾍⽂件主要是继承了scrapy.Spider类的,直接放上Spider类的源码
classSpider(object_ref):"""Base class for scrapy spiders. All spiders must inherit from this
class."""name=None
custom_settings=Nonedef __init__(self, name=None, **kwargs):if name is notNone:
self.name=nameelif not getattr(self, 'name', None):raise ValueError("%s must have a name" % type(self).__name__)
self.__dict__.update(kwargs)if not hasattr(self, 'start_urls'):
self.start_urls=[]
@propertydeflogger(self):
Logger(self.name)return logging.LoggerAdapter(logger, {'spider': self})def log(self, message,
level=logging.DEBUG, **kw):"""Log the given message at the given log level
This helper wraps a log call to the logger within the spider, but you
can use it directly (e.g. Spider.logger.info('msg')) or use any other
Python logger too."""self.logger.log(level, message,**kw)
@classmethoddef from_crawler(cls, crawler, *args, **kwargs):
spider= cls(*args, **kwargs)
spider._set_crawler(crawler)returnspiderdefset_crawler(self, crawler):
warnings.warn("set_crawler is deprecated, instantiate and bound the"
"spider to this crawler with from_crawler method"
"instead.",
category=ScrapyDeprecationWarning, stacklevel=2)assert not hasattr(self, 'crawler'), "Spider already bounded to a"\"crawler"self._set_crawler(crawler)def_set_crawler(self, crawler):
self.settings=crawler.settings
t(self.close, signals.spider_closed)defstart_requests(self):for url
inself.start_urls:yieldself.make_requests_from_url(url)defmake_requests_from_url(self, url):return Request(url,
dont_filter=True)defparse(self, response):raiseNotImplementedError
@classmethoddefupdate_settings(cls, settings):
settings.setdict(cls.custom_settingsor {}, priority='spider')
@classmethoddefhandles_request(cls, request):returnurl_is_from_spider(request.url, cls)
@staticmethoddefclose(spider, reason):
closed= getattr(spider, 'closed', None)ifcallable(closed):returnclosed(reason)def __str__(self):return "" %
(type(self).__name__, self.name, id(self))__repr__ = __str__
爬⾍⽂件⾥的类,都会继承这个类,这个类就是提供最基本的功能。对于其中的参数和⽅法,具体理解如下(是截取别⼈所说的):
name:
这个属性是字符串变量,是这个类的名称,代码会通过它来定位spider,所以它必须唯⼀,它是spider最重要的属性。回头看看源码中
__init__的定义,可以发现这个属性是可以修改的,如果不喜欢或者有需要重命名spider的name,可以在启动的时候传参修改name属性。
allowed_domains:
这个属性是⼀个列表,⾥⾯记载了允许采集的⽹站的域名,该值如果没定义或者为空时表⽰所有的域名都不进⾏过滤操作。如果url的域名不在这个变量中,那么这个url将不会被处理。不想使⽤域名过滤功能时可以在settings中注释掉OffsiteMiddleware, 个⼈不建议这么做。
start_urls:
这个属性是⼀个列表或者元组,其作⽤是存放起始urls,相当于这次任务的种⼦。使⽤默认模板创建spider时,该值是个元组,创建元组并且只有⼀个元素时需要在元素后⾯添加“,”来消除歧义,不然会报错:“ValueError: Missing scheme in request url: h”。这边经常有⼈出错,为了避免这个错误可以根据上⼀章内容,将模板中start_urls的值设置为列表。
custom_settings:
这个属性值是⼀个字典,存放settings键值对,⽤于覆盖项⽬中的settings.py的值,可以做到在⼀个项⽬中的不同spider可以有不同的配置。不过这个值要慎⽤,有些settings的值覆盖也没有起作⽤,eg:“LOG_FILE”。如果想每个spider都有⾃⼰的log⽂件的话就不能这么做。因为⽇志操作在这个⽅法执⾏之前,那么⽆论怎么改都改不了之前的⾏为。不过这个问题scrapy研发团队已经注意到了,相信不久的将来会进⾏处理的。
crawler:
这个值从源码可以看出来⾃于⽅法from_crawler()。该值是⼀个Crawler实例, 其作⽤后⾯的教程会讲解,这边就不细说了。
settings:
这个值也是来⾃于⽅法from_crawler()。是⼀个Settings实例,这个后⾯也会细说,稍安勿躁哈。
logger:
顾名思义,记录⽇志⽤的,也是后⾯讲,耐⼼等候哈。
from_crawler:
这是⼀个类⽅法,scrapy创建spider的时候会调⽤。调⽤位置在crawler.py 的类Crawler中,源码可以⾃⼰去看看,就不带⼤家看了。这个⽅法的源码在上⾯,我们可以看到,在实例化这个spider以后,这个实例才有的settings和crawler属性,所以在__init__⽅法中是没法访问这俩属性的。如果⾮要在__init__⽅法中使⽤相关属性,那么只能重写该⽅法,⼤家可以尝试写写。
start_requests():
这个⽅法必须返回⼀个可迭代对象,切记上⾯就有源码很简单,就不细说了。如果想对属性start_urls做⼀些操作(增删改),并希望结果作为种⼦url去采集⽹站的时候,可以重写这个⽅法来实现。有了这个⽅法,甚⾄都不⽤在代码中定义start_urls。⽐如我们想要读取持久化的url执⾏采集操作,那么就没
必要转存进start_urls⾥⾯,可以直接请求这些urls。当种⼦urls需要post请求的话,也需要重写该⽅法。
make_requests_from_url(url):
这个⽅法顾名思义,要是还不懂就看看上⾯的源码。这⾥只说⼀点,因为这⾥的Request初始化没有回调⽅法,就是默认采⽤parse⽅法作为回调。另外这⾥的dont_filter值为True,这个值的作⽤是该url不会被过滤,⾄于具体细节请听下回分解。
parse(self, response):
这个⽅法对于有经验的同学来说再熟悉不过了,我就简单的说说。这个⽅法作为默认回调⽅法,Request没有指定回调⽅法的时候会调⽤它,这个回调⽅法和别的回调⽅法⼀样返回值只能是Request, 字典和item对象,或者它们的可迭代对象。
log(message[, level, component]):
这个⽅法是对logger的包装,看看源码就好,没什么什么可说的。
closed(reason):
当这个spider结束时这个⽅法会被调⽤,参数是⼀个字符串,是结束的原因。这种⽤法以后会介绍,这⾥只需记住,想在spider结束时做⼀些操作时可以写在这⾥。
对于初学者,其中最主要需要查看的start_requests和make_requests_from_url⽅法了,这两个⽅法可以告诉你scrapy框架中爬⾍运⾏起来时Request和Response是怎样被传递的。对于我现在的理解来说,当启动scrpay爬⾍后,⾸先会根据设置来初始化,然后根据爬⾍⽂件中的start_requests和make_requests_from_url来发送Request,然后Request也会经过中间件处理,最后返回的Response由爬⾍⽂件中的parse接收,然后对Response进⾏处理,获取相应数据。因此可以通过中间件,以及修改start_requests和
make_requests_from_url、parse来达到⾃⼰想要的要求(或逻辑)。
最后,说下我最近学习爬⾍的⼼得吧,在获取页⾯源码到编写正则提取数据的过程或者是模拟登陆的过程中,都要通过适当的输出来调式程序,看哪⾥运⾏了哪⾥没有运⾏,都可以通过适当的输出来检查。然后对于过程中碰到的任何问题,都要很有耐⼼的去解决,通过查阅浏览器或者和他⼈讨论,来解决问题。写博客只是为了记录下最近的学习过程,其中有哪⾥说得不对的,希望看到这篇博客的⼈见谅,也希望看到这篇博客的⼤⽜们给我分享点经验和意见。这也是本⼈的第⼀篇博客,可能格式上有哪⾥看的不舒服的,希望可以见谅。希望这篇博客可以帮到你。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。