美文网首页我爱编程
模拟登陆163邮箱并爬取邮件信息 - Selenium和Mong

模拟登陆163邮箱并爬取邮件信息 - Selenium和Mong

作者: 马淑 | 来源:发表于2017-10-07 00:25 被阅读486次

归属文集:Selenium + MongoDB

环境需求

本机环境:32bit Windows + Python3 + Selenium + MongoDB ; FireFox浏览器;Firefox Driver

流程分析

  1. Selenium模拟输入账号和密码点击登陆163邮箱
  2. 等待网页加载,模拟点击收件箱获取邮件列表,并爬取初始页
  3. 模拟点击翻页按钮,爬取分页
  4. 数据存储至MongoDB

代码实现

注释很详细,直接上代码

import time
from time import sleep

import pymongo
from selenium import webdriver
from bs4 import BeautifulSoup

client = pymongo.MongoClient('localhost', 27017)
db = client['163mail']
collection = db['mymail']


'''以下代码实现数据保存'''
def save_to_db(item):
    if collection.insert_one(item):
        print('保存成功!')
    else:
        print('保存失败!')

'''以下代码实现数据提取'''
def parse_item(maillist):
    for m in maillist:
        long_text = str(m.attrs['aria-label'])
        yield {
            'e_title':long_text.split('发件人 :')[0].strip(),
            'e_sender':long_text.split('发件人 :')[1].split('时间:')[0].strip(),
            'e_send_time':long_text.split('发件人 :')[1].split('时间:')[1].strip()
        }


'''以下代码实现邮件列表Soup处理'''
def process_soup(soup, Count):
    try:
        maillist = soup.find_all(attrs={'sign': 'letter'})
        for item in parse_item(maillist):
            Count += 1
            print(Count, item)
            save_to_db(item)
    except Exception as err:
        print(err)
    finally:
        return Count

'''以下代码实现163邮箱自动登录'''
def login(driver):
    # 等待页面完全加载
    driver.implicitly_wait(30)

    # 因为163登录入口在iframe里面,所以先要切换到iframe
    frame = driver.find_element_by_id('x-URS-iframe')
    driver.switch_to.frame(frame)

    # 使用driver将账号密码填进表单并提交
    email = driver.find_element_by_xpath("//input[@name='email']").send_keys('tjmashu')
    pwd = driver.find_element_by_xpath("//input[@name='password']").send_keys('***********')
    login = driver.find_element_by_id('dologin').click()
    return driver

'''以下代码实现163邮箱收件箱 - 初始页邮件爬取'''
def crawl_1st_page(driver, Count):
    print('page = ' + '1')
    # 点击收件箱按钮加载邮件列表
    driver.find_element_by_id('_mail_component_76_76').click()
    time.sleep(20)
    driver.switch_to.default_content()
    html = driver.page_source
    soup = BeautifulSoup(html, 'html.parser')
    Count = process_soup(soup, Count)

    # 获取总的翻页数
    total_page = int(driver.find_element_by_class_name('nui-select-text').text.split('/')[1])
    print('total_page = ' + str(total_page))
    return [total_page, Count]

'''以下代码实现163邮箱收件箱 - 分页邮件爬取'''
def crawl_other_page(driver, Count):
    # 点击下一页按钮加载邮件列表
    ele = driver.find_element_by_class_name('nui-toolbar-ext')
    this_ele = ele.find_elements_by_class_name('nui-toolbar-item')[-2]
    mail_box = this_ele.find_elements_by_tag_name('div')[-1]
    mail_box.click()
    time.sleep(20)
    driver.switch_to.default_content()
    html = driver.page_source
    soup = BeautifulSoup(html, 'html.parser')
    Count = process_soup(soup, Count)
    print('page = ' + str(page + 1) + '分析完成')
    return Count

'''主程序'''
if __name__ == '__main__':
    Count = 0

    # 调用FireFox Driver登陆163邮箱
    driver = webdriver.Firefox()
    driver.implicitly_wait(30)
    driver.get('http://mail.163.com/')
    driver = login(driver)

    # 等待登陆后的新页面完全加载
    time.sleep(20)

    # 获取最新的窗口句柄,不然会发生can't access dead object错误
    driver.switch_to.default_content()

    # 先爬取初始收件箱页面并获取总的页数,再爬取分页页面
    total_page, Count = crawl_1st_page(driver, Count)

    # 获取每个分页上的邮件列表信息生成对应Soup,每个Soup逐一解析处理
    for page in range(total_page):
        time.sleep(20)
        driver.switch_to.default_content()
        if page > 0:
            print('page = ' + str(page+1))
            Count = crawl_other_page(driver, Count)

运行结果

问题与解决

1. Driver找不到Element错误

原因1:163邮箱页面加载响应时间过长,程序执行查找元素时,该元素还没有被加载
解决1:设置强制等待time.sleep(20),隐式等待driver.implicitly_wait(30)

原因2:要查找的内容是在iframe里面时,需要先找到iframe将句柄切换这个iframe
解决2:切换句柄到iframe

    # 因为163登录入口在iframe里面,所以先要切换到iframe
    frame = driver.find_element_by_id('x-URS-iframe')
    driver.switch_to.frame(frame)

原因3:Element是动态ID,没有name属性,driver.find_element_by_id()不可用
解决3:先向上查找到它的父元素,再往下定位要找的子元素,例如本例中向上两级找到了静态id的父元素,再向下回去找子元素

    # 点击下一页按钮加载邮件列表
    ele = driver.find_element_by_class_name('nui-toolbar-ext')
    this_ele = ele.find_elements_by_class_name('nui-toolbar-item')[-2]
    mail_box = this_ele.find_elements_by_tag_name('div')[-1]
    mail_box.click()
2.页面跳转更新之后,查找元素时报can't access dead object错误

原因:页面跳转更新后,要将句柄切换到新的页面内容
解决:增加语句 driver.switch_to.default_content()

3.出现[WinError 10061] 由于目标计算机积极拒绝,无法连接。

原因:MongoDB服务没有启用!
解决:以管理员身份启用MongoDB,输入:net start MongoDB启动MongoDB

4.有时候元素明明已经找到了,运行也没报错,点击后页面没任何反应。

网上有人用js方法解决这个问题,参见:http://blog.csdn.net/mufenglin01/article/details/72637043
也有人发现原来是click的时候已经失去该焦点了,解决办法是先找另外的元素,再来找这个元素,例如:
http://blog.csdn.net/aerchi/article/details/8061127

相关文章

网友评论

    本文标题:模拟登陆163邮箱并爬取邮件信息 - Selenium和Mong

    本文链接:https://www.haomeiwen.com/subject/bfkdyxtx.html