python srapy快速入门并改造之前的csdn代码

1. 先安装 scrapy,有虚拟环境的进入虚拟环境安装

pip install scrapy

2. 进入项目总目录(例如文件夹a),如果有虚拟环境,先启动

source ~/.bash_profile

3. 进入虚拟环境

workon xxx

4. 建立一个  scrapy 项目

scrapy startproject scrapy_test

5. 按照提示

    cd scrapy_test
    scrapy genspider csdn csdn.net

如上的话,会在spiders文件下,帮我们自动建立一个 example.py的文件。 我们也可以手动去写这个文件。2种方法都可以。

新建的文件代码如下

import scrapy


class CsdnSpider(scrapy.Spider):
    name = 'csdn'
    allowed_domains = ['csdn.net']
    start_urls = ['http://csdn.net/']

    def parse(self, response):
        pass

6. 怎么运行爬虫呢? 在命令行中运行如下(注意要在目录和虚拟环境中)

scrapy crawl csdn(上一步建立的爬虫名称)

启动的时候,可能会遇到一个错误, No module named 'win32api'

如果是 widows里面,去下载依赖包, https://www.lfd.uci.edu/~gohlke/pythonlibs/

如果是 mac和linux, 直接 pip install PyWin32 即可

7. 运行是可以了,那怎么写入代码逻辑呢?答,我们要把逻辑写到 csdn.py 中的 parse方法中。例如

def parse(self, response):
    print("已经下载到html")

如上,我们在命令行中,再启动一次如下代码,就可以看到命令行中有输出 “已经下载到html”

scrapy crawl csdn(上一步建立的爬虫名称)

8. 如上,每一次启动都在命令行中比较麻烦,我们来改进一下。我们目录下,新建一个文件 main.py,文件位置如下

注意main.py 的位置,然后再写入代码,要借助 scray.cmdline 的命令行方法,执行命令行

from scrapy.cmdline import execute  # 命令行方法

# 如下命令可以执行命令行
execute(["scrapy", "crawl", "csdn"])

9. 大概参数讲解

allowed_domains = ['csdn.net']
start_urls = ['http://csdn.net/']
  • allowed_domains  是被允许爬取的地址,如果我们的页面页面中有外链到其他网站了,如果没有在这里标注,也不被允许爬取
  • start_urls 这里是要所有要爬取的地址,我们可以写在其中,到时候scrapy会循环,然后parse是执行逻辑。

10. 在 parse 中写入逻辑,我们之前是用下面的方法

def parse_list(url):
    res_text = requests.get(url).text  # 获取url的内容
    sel = Selector(text=res_text)  # 获取selector对象
    # 我们找的信息,进过观察在 table下面的 tr中
    all_trs = sel.xpath("//table[@class='forums_tab_table']/tbody//tr")

现在在parse中不需要这么写了,sel直接就是response,scrapy已经帮我们做好了解析了

def parse(self, response):
    sel = response
    # 我们找的信息,进过观察在 table下面的 tr中
    all_trs = sel.xpath("//table[@class='forums_tab_table']/tbody//tr")

11. 改造之前的代码,之前用到 domain = "https://bbs.csdn.net", 改造如下

class CsdnSpider(scrapy.Spider):
    name = 'csdn'
    allowed_domains = ['csdn.net']
    start_urls = ['https://bbs.csdn.net/forums/ios']
    domain = "https://bbs.csdn.net"

下面parse中用到的domain都改成 self.domain

12. 之前还用到了,保存到数据库,我们在目录下新建一个 models.py 文件,注意目录位置,把之前代码拷贝进去

然后在 csdn.py 中引入

from scrapy_test.models import *

13. 我们在写获取列表的过程中,拿到帖子的连接,是要继续下载帖子的html然后解析的。如下

# 解析帖子内容页面
parse_topic(topic_url)

在scrapy中要改造成,如下,scrapy用yield这种写法,会自动让我们解析列表,然后把下载之后的页面给到我们自己定义的callback函数中,我们只需要专注页面的解析。

# 解析帖子内容页面
yield scrapy.http.Request(url=topic_url, callback=self.parse_topic)

url表示要下载的url, callback表示下载成功之后,进行的操作。我们在parse同级建立一个 parse_topic函数,调用的时候,注意要用,self.parse_topic, (注意函数位置),如下

14. 解析列表页,里面有一个判断是否下一页的。代码如下:

# 如果下一页存在,就取到下一页的连接,放入 next_url
if next_page:
    next_url = parse.urljoin(self.domain, next_page[0])
    # 继续解析刚刚得到的下一页的地址
    parse_list(next_url)

需要改造成,代码如下

# 如果下一页存在,就取到下一页的连接,放入 next_url
if next_page:
    next_url = parse.urljoin(self.domain, next_page[0])
    # 继续解析刚刚得到的下一页的地址
    yield scrapy.http.Request(url=next_url, callback=self.parse)

因为这个方法放在 parse中,所以这个callback也可以不写,默认回调就是 parse方法

# 如果下一页存在,就取到下一页的连接,放入 next_url
if next_page:
    next_url = parse.urljoin(self.domain, next_page[0])
    # 继续解析刚刚得到的下一页的地址
    yield scrapy.http.Request(url=next_url)

15. 继续改造解析内容的部分,原来代码如下

def parse_topic(self, response):
    # 解析topic详情页
    # 获取帖子的详情以及回复
    topic_id = url.split('/')[-1]
    res_text = requests.get(url).text
    sel = Selector(text=res_text)

这里的topic_id 是从url中得到的,这里改造之后,url 可以从 response.url 获取,如下

def parse_topic(self, response):
    # 解析topic详情页
    # 获取帖子的详情以及回复
    url = response.url
    sel = response

如上,sel=response,是因为之前代码下面都用到了sel,所以直接赋值,也可以不赋值,将下面的sel都改成 response 也可以。其他的改造如之前一样。

16. 爬取的过程中,发现要规避 网站的 robots 协议,我们在 scrapy下面的settings.py 中,将 ROBOTSTXT_OBEY改成False

17. 代码写到这里,我们把解析和保存的功能都写在 parse中,scrapy本身还给我们提供了 items.py 和 pipelines.py ,想做到parse中,直解析数据,保存数据这些功能分离出来。

第一步,我们先在 items.py 中新建类,这里是从原来的models中,复制过来,然后把类型都改成 scrapy.Field()类型

class TopicItem(scrapy.Item):
    itle = scrapy.Field()  # 标题
    content = scrapy.Field()  # 内容
    id = scrapy.Field()  # id
    author = scrapy.Field()  # 作者˚
    create_time = scrapy.Field()  # 创建时间
    answer_nums = scrapy.Field()  # 回复数量
    click_nums = scrapy.Field()  # 点击数量
    praised_nums = scrapy.Field()  # 点赞数
    jtl = scrapy.Field()  # 结帖率
    score = scrapy.Field()  # 赏分
    status = scrapy.Field()  # 状态
    last_answer_time = scrapy.Field()  # 最后回复时间

第二步,在csdn.py主流程中引入

第三步,将如下代码修改为

topic = Topic() 

修改为

topic_item = TopicItem()

然后将原来赋值的语句,如下

topic.status = status

修改为

topic_item['status'] = status

修改好之后,将原来的保存代码去掉,这个保存的工作放到 pipelines.py 中去做。然后在原来保存代码的地方,yield出去,如下

yield topic_item

ps: parse中,可以yield 2种类型,一种是 yield scrapy.http.Request() 做解析新页面用的,一种是 yield item类型,如果是第二种,则会进入pipelines.py 中进行处理。

那么,怎么在pipelines.py 中接收到 yield过来的值呢,这里要在 settings.py 中设置一下,在 settings.py 中搜索 pipelines.py中的ScrapyTestPipeline,如下代码,把注释去掉

ITEM_PIPELINES = {
   'scrapy_test.pipelines.ScrapyTestPipeline': 300,
}

18. 上面说了,yield item类型之后,会自动到 pipelines.py 中处理,所有的 yeild都会进入,那怎么区分呢,有2种方法,我们可以在items.py中,做如下配置

第一种方法,如下,在Item下面新建 to_model方法,然后在 pipelines.py 中调用即可(未测试)

第二种方法,把to_model修改成 save()方法,把整个逻辑放在这里处理

先在 items.py中引入 from scrapy_test.models import * 然后如下:


import scrapy
from scrapy_test.models import *


class ScrapyTestItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    pass


class TopicItem(scrapy.Item):
    title = scrapy.Field()  # 标题
    content = scrapy.Field()  # 内容
    id = scrapy.Field()  # id
    author = scrapy.Field()  # 作者˚
    create_time = scrapy.Field()  # 创建时间
    answer_nums = scrapy.Field()  # 回复数量
    click_nums = scrapy.Field()  # 点击数量
    praised_nums = scrapy.Field()  # 点赞数
    jtl = scrapy.Field()  # 结帖率
    score = scrapy.Field()  # 赏分
    status = scrapy.Field()  # 状态
    last_answer_time = scrapy.Field()  # 最后回复时间

    def save(self):
        topic = Topic()
        topic.title = self['title']
        topic.content = self['content']
        topic.id = self['id']
        topic.author = self['author']
        topic.create_time = self['create_time']
        topic.answer_nums = self['answer_nums']
        topic.click_nums = self['click_nums']
        topic.praised_nums = self['praised_nums']
        topic.jtl = self['jtl']
        topic.score = self['score']
        topic.status = self['status']
        topic.last_answer_time = self['last_answer_time']

        existed_topics = Topic.select().where(Topic.id == topic.id)
        if existed_topics:
            topic.save()
        else:
            topic.save(force_insert=True)

之后在 pipelines.py 中,调用 save(), 然后再 return item

class ScrapyTestPipeline(object):
    def process_item(self, item, spider):
        item.save()
        return item

19. 我们在调试的过程,发现一个错误,items.py中,赋值的时候,有些值当时没有获取到,如下

topic.answer_nums = self.['answer_nums']

要修改成如下, get方法的话,就算取不到,也会设置一个默认值

topic.answer_nums = self.get('answer_nums', 0)

20. User-Agent 添加到 scrapy中, 将如下代码

yield scrapy.http.Request(url=topic_url, callback=self.parse_topic)

修改成如下

from fake_useragent import UserAgent

ua = UserAgent()
request = scrapy.http.Request(url=topic_url, callback=self.parse_topic)
request.headers.setdefault("User-agent", ua.random)
yield request

如上写法,是可以的,但是比较繁琐,我们是想将所有的请求都带上 user-agent, 那么操作如下

我们在 middlewares.py 中,加上如下代码

from fake_useragent import UserAgent


class RandomUserAgentMiddleware(object):
    def process_request(self, request, spider):
        ua = UserAgent()
        request.headers.setdefault("User-Agent", ua.random)

这个时候要注意,这个是我们自己加的,所以要把 RandomUserAgentMiddleware 配置到 settings.py 中,大概在55行左右,把注释打开,然后替换我们自己写的类,如下

DOWNLOADER_MIDDLEWARES = {
   'scrapy_test.middlewares.RandomUserAgentMiddleware': 543,
}

后面的数字是优先级,越小越先执行,如上的话,就是统一设置了,所有的请求html都会加上 user-agent这个属性

21. 那么怎么设置代理ip呢?

还是在 中间件 middlewares.py 中 加入如下, 这个代码阿布云上面有参考

import base64

# 代理服务器
proxyServer = "http://http-dyn.abuyun.com:9020"

# 代理隧道验证信息
proxyUser = "H01234567890123D"
proxyPass = "0123456789012345"

proxyAuth = "Basic " + base64.urlsafe_b64encode(bytes((proxyUser + ":" + proxyPass), "ascii")).decode("utf8")


class ProxyMiddleware(object):
    def process_request(self, request, spider):
        request.meta["proxy"] = proxyServer
        request.headers["Proxy-Authorization"] = proxyAuth

然后在  settings.py 中 加入这个自定义的类

DOWNLOADER_MIDDLEWARES = {
   'scrapy_test.middlewares.RandomUserAgentMiddleware': 543,
   'scrapy_test.middlewares.ProxyMiddleware': 542,
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值