爬虫scrapy——网站开发热身中篇完结

 

#main.py放在scrapy.cfg同级下运行即可,与在控制台执行等效
import os
os.system('scrapy crawl books -o books.csv')
#第一讲:入门
import scrapy
class BooksSpider(scrapy.Spider):
    name = 'books' #本爬虫唯一标识
    start_urls = ['http://books.toscrape.com/'] #爬取页面
    def parse(self,response): #下载完后自动执行,提取数据及产生对下一个页面链接的下载请求
        for book in response.css('article.product_pod'):
            name = book.xpath('./h3/a/@title').extract_first()#使用XPath写法
            price = book.css('p.price_color::text').extract_first()#使用CSS选择器写法
            yield{
                'name': name,
                'price': price,
            }
        next_url = response.css('ul.pager li.next a::attr(href)').extract_first()
        if next_url:
            next_url = response.urljoin(next_url)
            yield scrapy.Request(next_url, callback=self.parse)
#第二讲:改start_urls为函数
import scrapy
class BooksSpider(scrapy.Spider):
    name = 'books'                                                  #本爬虫唯一标识
    def start_requests(self):
        yield scrapy.Request('http://books.toscrape.com/',
                            callback=self.parse,
                            headers={'User-Agent': 'Mozilla/5.0'},
                            dont_filter=True)
    def parse(self,response):                                       #下载完后自动执行,提取数据及产生对下一个页面链接的下载请求
        for book in response.css('article.product_pod'):
            name = book.xpath('./h3/a/@title').extract_first()      #使用XPath写法
            price = book.css('p.price_color::text').extract_first() #使用CSS选择器写法
            yield{
                'name': name,
                'price': price,
            }
        next_url = response.css('ul.pager li.next a::attr(href)').extract_first()
        if next_url:
            next_url = response.urljoin(next_url)
            yield scrapy.Request(next_url, callback=self.parse)      #产生请求
#第三讲:xml与css选择器
import scrapy
from scrapy.selector import Selector
from scrapy.http import HtmlResponse
text = '''

    
        
        example website
    
        
  • C++
  • JAVA++
  • python
helloCJGJLworld
  • aaa
  • bbb
  • ccc

hello

world

CJ GJL ''' #一、生成selector的两种方法(str与HtmlResponse) response=Selector(response=HtmlResponse(url='http://www.example.com',body=text,encoding='utf8')) print(response) selector=Selector(text=text) print(selector) # # print('--------------------------') #二、详解xml与xPath #1、xml文档节点类型:根节点,元素节点,属性节点,文本节点 #2、xml节点间关系:父子,兄弟,祖先,子孙 #3、xPath基本语法: # / 表示根节点或连接儿子,注意html是根节点的儿子,即/html # EMEMENT 当前节点的名为ELEMENT的子元素节点 # @ATTR 当前节点的名为ATTR的子属性节点 # text() 当前节点的子文本节点 print(response.xpath('/html')) #从根开始的绝对路径,获得html元素节点 print(response.xpath('/html/body/div/a').extract()) #从根开始的绝对路径,获得a元素节点 print(response.xpath('/html/body/div/a/text()').extract())#从根开始的绝对路径,获得a元素节点的文本节点 print(response.xpath('/html/body/div/a/@href').extract()) #从根开始的绝对路径,获得a元素节点的href属性节点 # [] # ['CJ', 'GJL'] # ['CJ', 'GJL'] # ['1314', '520'] # // 表示所有子孙节点 print(response.xpath('//div//img').extract()) #选中文档中所有div元素节点的所有img元素节点 print(response.xpath('//div//text()').extract()) #选中文档中所有div元素节点的所有文本节点 print(response.xpath('//div//@href').extract()) #选中文档中所有div元素节点的所有href属性节点 # ['', ''] # ['hello', 'CJ', 'GJL', 'world'] # ['1314', '520'] # * 当前元素节点所有子元素节点 # @* 当前元素节点所有子属性节点 # text() 当前元素节点所有子文本节点 print(response.xpath('//div/*').extract()) #选中文档中所有div元素节点的所有子元素节点 print(response.xpath('//div/@*').extract()) #选中文档中所有div元素节点的所有子属性节点 print(response.xpath('//div/text()').extract()) #选中文档中所有div元素节点的子文本节点 # ['CJ', 'GJL'] # ['images'] # ['hello', 'world'] # . 当前节点 # .. 当前节点父亲 print(response.xpath('//div').xpath('./text()').extract()) #第二次xpath以前当前选择器路径开始故用./表示相对路径 print(response.xpath('//img').xpath('..').extract()) #第二次xpath以前当前选择器路径开始故用..表示父亲节点 print(response.xpath('//img/..').extract()) #作用同上一行 # ['hello', 'world'] # ['CJ', 'GJL'] # ['CJ', 'GJL'] # [谓语] 附加条件 print(response.xpath('//a[2]').extract()) #a元素节点第2个(从1开始计) print(response.xpath('//a[last()]').extract()) #a元素节点最后1个 print(response.xpath('//a[position()<=1]').extract()) #a元素节点前1个 print(response.xpath('//div[@id]').extract()) #有id属性的div元素节点 print(response.xpath('//div[@id="images"]').extract()) #有id属性值为images的div元素节点 # ['GJL'] # ['GJL'] # ['CJ'] # ['
helloCJGJLworld
'] # ['
helloCJGJLworld
'] print('--------------------------') #三、提取数据的两种方法(extract_first与遍历,其实就是字典的val组成List然后取List中的各个str元素) print(selector.xpath('//li/text()')) #1、这只是生成一个元素为Selector的list print(selector.xpath('//li/text()').extract()) #2、要提取data的值,才生成一个元素为str的list print(selector.xpath('//b').extract_first()) #3、当list只有一个元素时,常用extract_first for sel in selector.xpath('//h1'):print(sel.xpath('./text()')) #4、当list有多个元素时,用for遍历访问 # [, , ] # ['C++', 'JAVA++', 'python'] # CJ GJL # [] # [] print('--------------------------') #四、CSS选择器 print(selector.css('img').extract()) #所有img print(selector.css('base,title').extract()) #所有base与title print(selector.css('body img').extract()) #所有body的子孙中的img print(selector.css('body>div').extract()) #所有body的儿子中的div print(selector.css('[id]').extract()) #所有有id属性的元素节点 print(selector.css('[id=images]').extract()) #所有有id属性且等于images的元素节点 print(selector.css('ul:nth-child(3)>li:nth-child(2)').extract())#所有作为其父亲的第三个儿子的ul的第二个儿子(叫li) print(selector.css('ul:first-child>li:last-child').extract()) #所有作为其父亲的第一个儿子的ul的最后一个儿子(叫li) print(selector.css('a::text').extract()) #所有a元素节点中的文本 # ['', ''] # ['', 'example website'] # ['', ''] # ['
helloCJGJLworld
'] # ['
helloCJGJLworld
'] # ['
helloCJGJLworld
'] # ['
  • bbb
  • '] # ['
  • python
  • '] # ['CJ', 'GJL'] 补充说明一下:第一讲中的下一页链接的CSS写法是'ul.pager li.next a::attr(href)' 源html是
    #第五讲:写入文件时转换及查重(与上一讲的两个文件结合,总共5个文件已改写了4个)
    #settings.py
    ITEM_PIPELINES = {
       'example.pipelines.PriceConverterPipiline': 300,
       'example.pipelines.DuplicatesPipiline': 350,
    }
    #pipelines.py
    from scrapy.exceptions import DropItem
    class DuplicatesPipiline(object):
        def __init__(self):
            self.book_set = set()               #初始化用于书名去重集合
        def process_item(self,item,spider):
            name = item['name']
            if name in self.book_set:
                raise DropItem('Duplicate book found: %s' % item)#抛出异常就不会再写入文件
            self.book_set.add(name)
            return item
    class PriceConverterPipiline(object):
        exchange_rate = 8.5309
        def process_item(self,item,spider):                     #传入Item或dict
            price=float(item['price'][1:])*self.exchange_rate   #去掉前面英磅符号转浮点数再乘汇率
            item['price']='RMB %.2f' % price                    #重新赋值为人民币符号加保留2位小数的浮点数
            return item                                         #传出item或dict
    
    #第六讲:提取链接(个人感觉作用不大)
    #用Selector是可以的,下面的例子是使用LinkExtractor,其实差不多
            from scrapy.linkextractors import LinkExtractor
            links = LinkExtractor(restrict_css='ul.pager li.next').extract_links(response)
            if links:
                next_url = links[0].url
                yield scrapy.Request(next_url,callback=self.parse)
    '''
            next_url = response.css('ul.pager li.next a::attr(href)').extract_first()
            if next_url:
                next_url = response.urljoin(next_url)
                yield scrapy.Request(next_url, callback=self.parse)
    '''
    #第七讲:自定义导出格式(scrapy crawl books -o books.csv中的csv格式其实够用)
    #my_exporters.py(与settings.py同级)
    from scrapy.exporters import BaseItemExporter
    import xlwt
    class ExcelItemExporter(BaseItemExporter):
        def __init__(self, file, **kwargs):
            self.configure(kwargs)
            self.file = file
            self.wbook = xlwt.Workbook()
            self.wsheet = self.wbook.add_sheet('scrapy')
            self.row = 0
        def finish_exporting(self):
            self.wbook.save(self.file)
        def export_item(self, item):
            fields = self.__get_serialized_fields(item)
            for col, v in enumerate(x for _, x in fields):
                self.wsheet.write(self.row, col, v)
            self.row += 1
    #settings.py
    FEED_EXPORTERS = {
       'excel': 'example.my_exporters.ExcelItemExporter'
    }
    #第八讲:分析页面
    scrapy shell http://matplotlib.org/examples/index.html
    
    In [1]: view(response)
    Out[1]: True
    
    In [2]: from scrapy.linkextractors import LinkExtractor
    
    In [3]: le=LinkExtractor(restrict_css='div.toctree-wrapper.compound li.toctree-l2')
    
    In [4]: links=le.extract_links(response)
    
    In [5]: [link.url for link in links]
    Out[5]: 
    ['https://matplotlib.org/examples/animation/animate_decay.html',
     'https://matplotlib.org/examples/animation/basic_example.html',
     'https://matplotlib.org/examples/animation/basic_example_writer.html',
    ……
     'https://matplotlib.org/examples/widgets/slider_demo.html',
     'https://matplotlib.org/examples/widgets/span_selector.html']
    
    In [6]: len(links)
    Out[6]: 506
    
    In [7]: fetch('https://matplotlib.org/examples/animation/animate_decay.html')
    2020-09-13 10:57:26 [scrapy.core.engine] DEBUG: Crawled (200)  (referer: None)
    
    In [8]: view(response)
    Out[8]: True
    
    In [9]: href=response.css('a.reference.external::attr(href)').extract_first()
    
    In [10]: href
    Out[10]: 'animate_decay.py'
    
    In [11]: response.urljoin(href)
    Out[11]: 'https://matplotlib.org/examples/animation/animate_decay.py'
    
    #第九讲:爬取图片(爬取失败,原因未知)
    scrapy startproject so_image
    cd so_image
    scrapy genspider images image.so.com
    
    #images.py
    import scrapy
    from scrapy import Request
    import json
    class ImagesSpider(scrapy.Spider):
        BASE_URL='http://image.so.com/zj?ch=art&sn=%s&listtype=new&temp=1'
        start_index=0
        MAX_DOWNLOAD_NUM=1000
        name = 'images'
        start_urls = [BASE_URL % 0]
        def parse(self, response):
            infos=json.loads(response.body.decode('utf-8'))
            yield {'image_urls':[info['qhimg_url'] for info in infos['list']]}
            self.start_index+=infos['count']
            if infos['count']>0 and self.start_index
    #第十讲:爬取文件(爬取失败,原因未知)
    创建项目
    scrapy startproject matplotlib_examples
    
    进入目录
    cd matplotlib_examples
    
    定义爬虫名字及要爬的域名
    scrapy genspider examples matplotlib.org
    
    ***下面依次修改items,examples,middlewares,pipelines,settings五个py文件***
    
    首先,改items.py,确定要爬的数据内容,后面examples.py要导入使用
    import scrapy
    class ExampleItem(scrapy.Item):
        file_urls=scrapy.Field()
        files=scrapy.Field()
    
    再者,改examples.py实现爬取
    import scrapy
    from scrapy.linkextractors import LinkExtractor
    from ..items import ExampleItem
    class ExamplesSpider(scrapy.Spider):
        name = 'examples'
        allowed_domains = ['matplotlib.org']
        start_urls = ['http://matplotlib.org/examples/index.html']
        def parse(self, response):
            le=LinkExtractor(restrict_css='div.toctree-wrapper.compound',deny='/index.html$')
            print(len(le.extract_links(response)))
            for link in le.extract_links(response):
                yield scrapy.Request(link.url,callback=self.parse_example)
        def parse_example(self,response):
            href=response.css('a.reference.external::attr(href)').extract_first()
            url=response.urljoin(href)
            example=ExampleItem()
            example['file_urls']=[url]
            return example
    
    然后,不用改middlewares.py,除非用到代理服务器
    
    接着,改piplelines.py,自定义存储函数(主要功能是重命名下载的文件名)
    from scrapy.pipelines.files import FilesPipeline
    from urllib.parse import urlparse
    from os.path import basename,dirname,join
    class MyFilesPipeline(FilesPipeline):
        def file_path(self,request,response=None,info=None):
            path=urlparse(request.url).path
            return join(basename(dirname(path)),basename(path))
    
    最后,改settings.py,前四行是自动生成的,就改ITEM_PIPELINES字典即可
    BOT_NAME = 'matplotlib_examples'
    SPIDER_MODULES = ['matplotlib_examples.spiders']
    NEWSPIDER_MODULE = 'matplotlib_examples.spiders'
    ROBOTSTXT_OBEY = True
    ITEM_PIPELINES = {
       'matplotlib_examples.pipelines.MyFilesPipeline': 1,
    }
    FILES_STORE='examples_src'
    
    #对了,本项目是我认真对照过书本,但是爬取失败了不知为何,但代码理解了就好
    
    关于scrapy的使用思路总结:
    ·在第一章中,只改动examples.py这个主要实现爬虫的文件就可以爬了,middlewares.py默认就是不动的
    ·后面改items是为了明确要爬的信息还有让pipelines.py的代码可以操作这个数据实现自定义的下载方法
    ·而如果要用自pipelines.py中自己写的函数,settings.py中就要改动ITEM_PIPELINES字典进行显式声明
    
    关于未总结的三个章节内容:
    ·关于js动态网页的爬取,安装js渲染引擎unsplash时那个pywin32版本有冲突,我怕我的Kivy跑不了所以没有装,用到再研究,好像疯狂讲义里有讲到模拟浏览器运行来爬取!
    ·关于代理服务器的爬取,我没有代理服务器,暂时跳过
    ·关于redis分布式爬取,我没有装redis,也没有多台电脑,暂时跳过

     

    你可能感兴趣的:(2020年暑假研零笔记)