helloworld000
V2EX  ›  问与答

请问 scrapy 爬虫大佬 关于 spider 要在多个页面下跳转提取 item 不同的 field, yield 出去 最后存取最终 item 的问题

  •  
  •   helloworld000 · Aug 22, 2020 · 1791 views
    This topic created in 2264 days ago, the information mentioned may be changed or developed.

    第一次用 scrapy, 请多多指教.

    用一个爬 stackoverflow 用户数据的例子来举例说明, 我的 spider 主要核心逻辑是这样的:

    • 先到 user 页面拿到所有 users;
    • 然后到每个 user 页面拿到 user 信息, 放入 item, 并且提取出这个 user 的 top-10 posts
    • 然后去这 10 个 posts 的页面, 提取出这个 user post 的内容, 也放入 item 最终保存给 pipeline, 此时跳转 users 页面下一页

    详细步骤是下面这个 (前面 4 步都 ok 没问题, 主要问题在第 5 步)

    1. 从这个 url 开始 start_urls = ['https://stackoverflow.com/users']

    2. 第一个 parse 把这个页面所有 users 拿到, 然后一个 for loop 对每个 user 提取基本信息, 比如头像,用户名等, 放入 item 里, 然后在这个 for loop 里用 yield scrapy.Request() 进入每个 user 的页面, 并且把之前的 item 用 meta 也传了过去, 也写了一个 parseUserPage 的函数, 一起在 request 里 callback 了. 此外, 在这个 parse 函数 for loop 遍历 users 外面的最最下面, 还有一个跳转下一页的 yield scrapy.Request()

    3. 在那个 parseUserPage 的函数里, 提取用户的自我介绍这种信息, 放入 item 里, 再拿到这个用户页面里 top10 的 posts 的 urls, 因为想把所有 posts 信息都放在一起, 所以我先让这个 item 里['posts']这个 field 新建了一个空的 list, 然后再一个 for loop 对提取的 top10 的 posts 一一遍历. // 前面的步骤都没有任何问题, 都能在 pipelines.py 的 process_item()里用 logger 打印出 item 并且跳转到下一页面, 就是到了第三步再一次进行跳转到 post 页面的时候开始出幺蛾子了

    4. 对每一个 post 的 url 再一次用 yield scrapy.Request() 进入每个 post 的页面. 还是一样, 把之前的 item 用 meta 也传了过去 (此外, 我知道 meta 是用的 shallow copy, 而且我那个 post 用的是 list, 所以我改成了 deepcopy(item) ) 也写了一个对应的 parsePost 的函数用来 callback

    5. 在这个 parsePostPage 的函数里, 对这个问题回答页面, 我提取出所有对应的问题和答案, 找出和 user-id 对应的 答案或者问题信息抓取, 然后放入 item 里 post field 里, 因为用的是 list, 所以我用的是 append(), 找到后直接 break 这个 for loop, 然后这个 parsePostPage 函数结尾 yield 出最终 item

    其他信息都没问题, 比如用户名字和基本介绍这些, 问题就出现在最后处理 post 的时候, 按道理我是应该能把 top-10 个 posts 的内容都能放进我的 item 里 post 这个 field 里, 可为什么每次打印出来的结果只有个空 list, 或者只拿到 1 个 post 的内容???

    到底是什么原因造成的只能拿到一个空 list 或者只有一个 post?

    我测试的时候, 在 praseUserPage 都能拿到 10 个 top-posts 的 urls, 但是为什么对 top-10 posts 的那个 for loop 没有跑完就存取 item 了?

    我的尝试和猜想

    • 我是应该在 parseUserPage 的 for loop 遍历完所有 top posts 的下面去 yield item 吗? 试了一下, 这样前面很多 users 就是空的 list 什么 post 内容都没拿到, 感觉应该还是在最后一个拿到 item 所有信息的函数里 yield 出 item

    • 我试了一下在最后那个 parseUsePost 的函数里, 对一个问答页面的 post 进行 for 循环找到 user 的 post 并且将其提取出来 append 到 item 的 post 里的 list 后, 不 break 这个 for loop, 改成直接 yield item.

    这样 item 里就没有空 list 出现了, 但还是只能拿到一个或者 2 个 post 的情况.....在 parseUserPage 的函数里那个处理 top-10 的 for loop 没有把剩余的 9 个 posts 后面的遍历完...

    不太明白为什么会出现这个情况...希望 scrapy 大佬能解惑一下, 多谢!

    9 replies  •  2020-08-22 09:05:53 +08:00
    helloworld000
    helloworld000
    OP
    Aug 22, 2020
    1
    我又试了一下在最开始初始化 item 的时候就初始化这个 post 的 filed, 并且初始化为一个空的 list, 并且在第一次 yield 到 praseUserPage 的时候就 deepcopy 这个 item/


    最后在 pipeline 里的 process_item() 里打印出来的 logger 信息就是同一个 user 的 item 在不同时候被打印出来了 10 次, 并且每次都是带着不同的 post...其他信息都没有问题, 就这个 post 每次都不一样...而且只有一个, 应该是读取到了 10 个 posts, 但是为什么没有把最这 10 个 post 都存到一个 list 里去??


    如果我想把这 10 个 posts 都放到一个 list 里 (或者其他数据结构里?) 去要怎么办?
    helloworld000
    helloworld000
    OP
    Aug 22, 2020
    2
    最后在 scrapy 写入数据库的时候, 难道不是应该在 pipeline 的 process_item 里一次把 item 里全部写入吗?
    ooh
    ooh
    Aug 22, 2020
    3
    您第 4 步直接用 requests 库 for 循环请求这 10 个 post 完成后后把 item yield 到 pipeline 里面就是 10 个。
    helloworld000
    helloworld000
    OP
    Aug 22, 2020
    4
    @ooh 感谢回复!

    您意思是我第 4 步在 parseUserPage 里面的时候不用 yield scrapy.Request() 跳转到另外 post 页面?

    而是直接用 python 的 request 库 在 for loop 分别去处理 10 个 post 的页面, 然后加到 item 里去, 然后还是在 parseUserPage 的最后 yield item?
    helloworld000
    helloworld000
    OP
    Aug 22, 2020
    5
    @ooh 感谢...搞定了...
    ooh
    ooh
    Aug 22, 2020
    6
    @helloworld000
    如果是我会先用一个 user spider 类似下面这样的把所有 user 先入库可以只保存 user_id nickname

    class UserSpider(CrawlSpider):
    name = 'user'
    allowed_domains = ['stackoverflow.com']
    start_urls = ['https://stackoverflow.com/users']
    rules = (
    Rule(LinkExtractor(allow=('/users?page=(\\d+)&tab=reputation&filter=week')), callback='parse_item', follow=True),
    )

    然后再用一个 question spider 从 users 里面生成用户页面的 start_urls 抓取用户详情页面 先把你要的用户信息 yield 到 pipeline 里面根据 item type 更新用户数据,再 foreach yield 请求 question 页面,用 user_id 来关联
    Kobayashi
    Kobayashi
    Aug 22, 2020 via Android
    7
    把 user 和 post 拆成不同的 item,存储到数据库后再处理。不要再 scrapy 里用什么阻塞的 requests,异步优势全没了。
    helloworld000
    helloworld000
    OP
    Aug 22, 2020
    8
    @ooh 感谢大神! 第一次用 scrapy 不太熟悉让你见笑了

    请问你说的用另外一个 spider (question spider)去提取前一个 spider (user spider) 生成的 urls, 再进行关联.

    这个关联是在哪一步做的?在 pipeline 写另外一个函数吗?





    另外, 不过我还是非常好奇为什么之前用 scrapy.request 不行? 是不是之前哪个地方 yield 顺序错了?
    helloworld000
    helloworld000
    OP
    Aug 22, 2020
    9
    @Kobayashi 感谢回复, 请问拆成不同的 items, 比如我需要 top10 的 posts, 那么就是在 post 这个 item 里预留 10 个 field ? 能有好点的解决方法一个 filed 装下所有的 posts 吗? 我用 list 之前就是每次 yield item 都只能保存一个...
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   1310 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 19:58 · PVG 03:58 · LAX 11:58 · JFK 14:58
    ♥ Do have faith in what you're doing.