爬虫:58大规模爬取准备阶段

成果:

包含了预览页面信息和详情页面信息

代码:

from bs4 import BeautifulSoup
from pymongo import MongoClient
import time
import requests

# 这个是创建实例
client = MongoClient()
# 这个是创建一个phone_info表
phone_info = client['phone_url']
# 这个是创建表里面的字段,需要注意的是,字段里面才存储title,url这些东西
phone_link = phone_info['phone_link']
phone_details = phone_info['phone_details']


def formatted(string):
    return string.replace("\n", "").replace("\t", "").replace(" ", "")


def num_links(channel, pages):
    # 'http://bj.58.com/shoujihao/pn1/'
    url = '{}pn{}/'.format(channel, pages)
    r = requests.get(url)
    time.sleep(1)
    soup = BeautifulSoup(r.text, 'lxml')
    end_mark = soup.select('#infocont > span > b')[0].get_text()
    if int(end_mark) != 0:
        titles = soup.select('[class=boxlist] > ul > li > a.t > strong')
        links = soup.select('[class=boxlist] > ul > li > a.t')
        for t, l in zip(titles, links):
            title = t.get_text()
            link = l.get('href')
            data = {
                'title': title,
                'link': link,
            }
            # phone_link是字段phone_link在py中的使用对象,插入数据
            # 可以插入数据,就如下这样
            phone_link.insert({'title': title, 'link': link})
    else:
        pass


def get_phone_info(url):
    r = requests.get(url)
    time.sleep(1)
    soup = BeautifulSoup(r.text, 'lxml')
    title = soup.select('#main > div.col.detailPrimary.mb15 > div.col_sub.mainTitle > h1')
    price = soup.select(
        '#main > div.col.detailPrimary.mb15 > div.col_sub.sumary > ul > li > div.su_con > span.price.c_f50')
    title_text = title[0].get_text()
    price_text = price[0].get_text()
    data = {
        'url': url,
        'title': formatted(title_text),
        'price': formatted(price_text)
    }
    phone_details.insert(data)


def main():
    for page in range(1, 3):
        num_links('http://bj.58.com/shoujihao/', page)

    for info in phone_link.find():
        print(info['link'])
        url = info['link']
        get_phone_info(url)


if __name__ == '__main__':
    main()

大体思路:

之所以说这是准备阶段,目的之一就是爬取58上的所有信息,所以就要对于每个频道进行爬取。
而每个频道打开是预览页,预览页打开是详情页。所以,策略还是先爬取所有的预览页,存入mongodb然后再从mongodb取出link爬取详情页。基本还和之前一样,但是有几点是不一样的:

  • 加入了end_mark策略。预览页由于不知道究竟多少页结束,所以我们尽可能选取非常大的pg来看看和正常页面的区别,然后取其中的不同来作为mark
  • 不是for循环嵌套for循环来爬取了。而是通过数据库这个媒介来连接预览页和详情页。之前是爬取一个预览页面,就爬取这个预览页面的详情,再爬取第二个预览页面,接着爬取第二个预览页面的详情……现在是爬取所有的预览页面,存入数据库,然后这个完成之后再从数据库提取url爬取详情。
  • 还有一点没有考虑,如果爬取时间过长,有些手机号被交易了,则爬取的时候会报告404错误,导致失败。这一点来看是没有加入相应策略。

新技能get:

  • soup.find(‘a’)
    是找到第一个符合要求的标签,即便soup中有很多,只是返回第一个以add的形式返回而不是列表,后边可以加参数soup。find('a', id='123')的形式找出唯一符合的元素,
    如果是某个类的话,用soup.find('a', 'da')表示,为什么不用class='da'呢?因为class是Python的关键词,又因.da亲测无用,这个记住就可以了
  • soup.find()和soup.find_all()
    前者讲了,后者soup.find_all('a')则返回一个列表[aa,b]突然发现find_all()也可以有参数find_all('a', 'e'),就表示了这个e类。
  • soup.select()之class全匹配
    在这个路径复制出来就是a.hello.world,hello和world之间不是空格,是小数点,很神奇!!
    如果还有个类那么用soup.select('.hello')会搜索出来所有包含hello的class,而不是全匹配,就是说class=“hello world”也会被选出来。如果要用全匹配仅仅选出来class='hello'的,就用soup.select('[class=hello]' > * >^)加上[]就好了,全匹配
  • 字符串美化
    抓取出来的一些字符串有很多空格或者换行符,用字符串的str.replace('pre', 'post')来将pre的字串换为post的
  • pymongo的一些操作
    1.插入元素phone_details.insert(data),其中phone_details是个表,data必须是字典的格式,当然也可以phone_link.insert({'title': title, 'link': link})方式写入。
    还有个phone_details.save(data),如果data存在,则更新;如果不存在,则插入
    2.提取元素
    phone_link.find()提取所有的信息,但是必须用for info in phone_link.find()来显示,用info[‘url’]来显示具体的。或者筛选具备一些条件的:phone_link.find({'name': 'lucy', 'age':17})多个条件用逗号来隔开

总结:

这仅仅是爬取手机号的一个分支,如果大规模爬取,还有一个main的函数,将58的首页所有channel保存到一个数据库,在每个channel分别爬取,这个在以后会说。
这次学到不少东西,加油

你可能感兴趣的:(爬虫:58大规模爬取准备阶段)