Scrapy,使用自定义格式

时间:2017-11-04 11:25:42

标签: scrapy

一些背景知识。
我正在抓页每页有25个添加。
如果有超过25个添加,您有下一页的下一个按钮,依此类推。
每个添加都可以单独打开,以查看更多信息,但我没有这样做,因为我需要的所有信息都在添加列表的页面上。
我正在制作程序,将从昨天收集所有添加,然后将它们发送到电子邮件。
基本的想法是,人们不需要每天都检查新增加的内容,但会增加他(通过电子邮件)。

问题是如何进行Scrapy?

我已经完成了刮擦,工作正常,唯一要做的就是将这些项目发送到带有自定义格式的电子邮件。
通过自定义格式,我的意思是所有添加都有价格,我想在我发送的电子邮件中按价格订购。
我的想法很少,但不知道什么是正确/最好的方法,所以想要一些反馈 对于已经这样做的人,他/她知道所有的陷阱。

可能的解决方案:
1.将所有导出到JSON文件,然后在另一个脚本中导入带有pandas的JSON文件,按价格进行排序并将其发送到电子邮件。
- 我不喜欢有另外一个脚本和熊猫只是为了订购,但我也不喜欢拧我自己的订购功能,因为也许我将来会做更多的自定义格式。
2.导出到内存中的SQLite,可能在管道中(但目前我不知道如何甚至可能) - 对我来说看起来更好,因为我不需要另一个脚本而且SQl有订购,但我不知道在Scrapy中我可以访问所有被刮的物品吗?
3.其他一些想法?

2 个答案:

答案 0 :(得分:3)

由于您说您每天只启动一次抓取工具并且您希望每天发送一次电子邮件,因此您可以使用自定义Pipeline发送电子邮件并缓冲管道中的电子邮件,直到它为止蜘蛛完了。

管道从方法process_item接收来自蜘蛛的项目,因此您将在那里收集它们并将它们存储在管道内的列表中(管道被加载一次并且将通过您的蜘蛛运行存在,所以在管道中有缓冲区是可以的。)

当蜘蛛完成后,它将调用管道的close_spider方法,以便您可以发送邮件。

这是我用于我的一个系统的类(我简化了它并删除了诸如最大缓冲区大小,AWS相关代码等...):

class SendMailPipeline(object):
    def __init__(self, server, sender_mail, jinja_env):
        self.server = server
        self.sender = sender_mail
        self.items_cache = []
        self.cache_size = cache_size
        self.jinja = jinja_env

    @classmethod
    def from_crawler(cls, crawler):
        settings = crawler.settings

        server = smtplib.SMTP(settings['MAIL_SERVER'])
        server.ehlo()
        server.starttls()
        server.ehlo()
        server.login(settings['MAIL_USER'], settings['MAIL_PASSWORD'])

        # I used S3 in my solution to store the mail templates, but
        # I removed all AWS related stuff from this example
        # for simplification
        # feel free to use a standard jinja2 loader
        # (from local file system)
        jinja_env = jinja2.Environment(
            loader=skyscraper.jinja2.loaders.S3Loader(
                s3,
                settings['MAIL_TEMPLATE_BUCKET'],
                settings['MAIL_TEMPLATE_PREFIX']
            ),
            autoescape=jinja2.select_autoescape(['html', 'xml'])
        )

        return cls(server, settings['MAIL_FROM'], jinja_env)

    def process_item(self, item, spider):
        self.items_cache.append(item)

        return item

    def close_spider(self, spider):
        # When the spider is finished, we need to flush the cache one last
        # time to make sure that all items are sent
        self._flush_cache()

    def _flush_cache(self):
        items = self.items_cache

        if len(items) > 0:
            self.items_cache = []  # reset cache to empty
            self._send_mail(items)

    def _send_mail(self, items):
        # mailing_options was originally given from a database
        # feel free to get it from any system you like
        # or set a hard-coded path

        template_path = mailing_options['TemplatePath']
        recipients = mailing_options['Recipients']
        subject = mailing_options['Subject']

        template = self.jinja.get_template(template_path)

        mail_content = template.render(items=items)
        msg = MIMEText(mail_content)
        msg['Subject'] = subject
        msg['From'] = self.sender
        msg['To'] = ', '.join(recipients)

        self.server.send_message(msg)

此示例执行HTML邮件,但如果您阅读有关使用Python发送邮件的信息,则将HTML添加到邮件中应该非常简单。

答案 1 :(得分:0)

来自我方的解决方案,也许对某人有用 我使用DB和yagmail的数据集来发送带有Gmail帐号的电子邮件 我认为jinja有点矫枉过正: - )

class SendEmailPipeline(object):
    def open_spider(self, spider):
        db = dataset.connect('sqlite:///:memory:')
        self.table = db['tmp_table']

    def process_item(self, item, spider):
        # need to use dict(item)
        self.table.insert(dict(item))

    def close_spider(self, spider):
        spider.logger.info('SendEmailPipeline.close_spider()')
        # get number of adds
        # must do like this, because it is iterator/generator
        number_of_adds = 0
        for row in self.table.find(order_by=['price_per_m2', 'price_euro']):
            number_of_adds += 1

        # jinja template
        from jinja2 import Template
        template = Template(
        """
        <b>There are {{number_of_adds}} new ads:</b>
        {% for row in data %}
            {{row['price_per_m2']|round|int}} euro/m2 {{row['price_euro']|round|int}} euro {{row['area_in_m2']|round|int}}m2
            <a href="{{row['url']}}">{{row['title']}}</a>
        {% endfor %}
        """
        )

        email_body = template.render(data=self.table.find(order_by=['price_per_m2', 'price_euro']), number_of_adds=number_of_adds)

        subject = str(number_of_adds) + ' new adds'

        # send email 
        import yagmail
        yag = yag = yagmail.SMTP("GMAIL_EMAIL", "PASSWORD")
        yag.send(to='EMAIL_TO_SEND', subject=subject, contents=email_body)