精华内容
下载资源
问答
  • 原标题:使用 Python 编写多线程爬虫抓取百度贴吧邮箱与手机知道大家过年都是怎么过的,反正栏主是在家睡了一天,醒来的时候登QQ发现有人找我要一份贴吧爬虫的源代码,想起之前练手的时候写过一个抓取百度贴吧...

    原标题:使用 Python 编写多线程爬虫抓取百度贴吧邮箱与手机号

    不知道大家过年都是怎么过的,反正栏主是在家睡了一天,醒来的时候登QQ发现有人找我要一份贴吧爬虫的源代码,想起之前练手的时候写过一个抓取百度贴吧发帖记录中的邮箱与手机号的爬虫,于是开源分享给大家学习与参考。

    需求分析:

    本爬虫主要是对百度贴吧中各种帖子的内容进行抓取,并且分析帖子内容将其中的手机号和邮箱地址抓取出来。主要流程在代码注释中有详细解释。

    测试环境:

    代码在Windows7 64bit,python 2.7 64bit(安装mysqldb扩展)以及centos 6.5,python 2.7(带mysqldb扩展)环境下测试通过

    环境准备:

    工欲善其事必先利其器,大家可以从截图看出我的环境是Windows 7 + PyCharm。我的Python环境是Python 2.7 64bit。这是比较适合新手使用的开发环境。然后我再建议大家安装一个easy_install,听名字就知道这是一个安装器,它是用来安装一些扩展包的,比如说在python中如果我们要操作mysql数据库的话,python原生是不支持的,我们必须安装mysqldb包来让python可以操作mysql数据库,如果有easy_install的话我们只需要一行命令就可以快速安装号mysqldb扩展包,他就像php中的composer,centos中的yum,Ubuntu中的apt-get一样方便。

    相关工具可在我的github中找到:,其中easy_install的安装只需要在python命令行下运行那个py脚本然后稍等片刻即可,他会自动加入Windows的环境变量,在Windows命令行下如果输入easy_install有回显说明安装成功。

    环境选择的细节说明:

    至于电脑硬件当然是越快越好,内存起码8G起步,因为爬虫本身需要大量存储和解析中间数据,尤其是多线程爬虫,在碰到抓取带有分页的列表和详情页,并且抓取数据量很大的情况下使用queue队列分配抓取任务会非常占内存。包括有的时候我们抓取的数据是使用json,如果使用mongodb等nosql数据库存储,也会很占内存。

    网络连接建议使用有线网,因为市面上一些劣质的无线路由器和普通的民用无线网卡在线程开的比较大的情况下会出现间歇性断网或者数据丢失,掉包等情况,这个我亲有体会。

    至于操作系统和python当然肯定是选择64位。如果你使用的是32位的操作系统,那么无法使用大内存。如果你使用的是32位的python,可能在小规模抓取数据的时候感觉不出有什么问题,但是当数据量变大的时候,比如说某个列表,队列,字典里面存储了大量数据,导致python的内存占用超过2g的时候会报内存溢出错误。

    如果你准备使用mysql存储数据,建议使用mysql5.5以后的版本,因为mysql5.5版本支持json数据类型,这样的话可以抛弃mongodb了。(有人说mysql会比mongodb稳定一点,这个我不确定。)

    至于现在python都已经出了3.x版本了,为什么我这里还使用的是python2.7?我个人选择2.7版本的原因是自己当初很早以前买的python核心编程这本书是第二版的,仍然以2.7为示例版本。并且目前网上仍然有大量的教程资料是以2.7为版本讲解,2.7在某些方面与3.x还是有很大差别,如果我们没有学过2.7,可能对于一些细微的语法差别不是很懂会导致我们理解上出现偏差,或者看不懂demo代码。而且现在还是有部分依赖包只兼容2.7版本。我的建议是如果你是准备急着学python然后去公司工作,并且公司没有老代码需要维护,那么可以考虑直接上手3.x,如果你有比较充裕的时间,并且没有很系统的大牛带,只能依靠网上零零散散的博客文章来学习,那么还是先学2.7在学3.x,毕竟学会了2.7之后3.x上手也很快。

    多线程爬虫涉及到的知识点:

    其实对于任何软件项目而言,我们凡是想知道编写这个项目需要什么知识点,我们都可以观察一下这个项目的主要入口文件都导入了哪些包。

    现在来看一下我们这个项目,作为一个刚接触python的人,可能有一些包几乎都没有用过,那么我们在本小节就来简单的说说这些包起什么作用,要掌握他们分别会涉及到什么知识点,这些知识点的关键词是什么。这篇文章并不会花费长篇大论来从基础讲起,因此我们要学会善用百度,搜索这些知识点的关键词来自学。下面就来一一分析一下这些知识点。

    HTTP协议:

    我们的爬虫抓取数据本质上就是不停的发起http请求,获取http响应,将其存入我们的电脑中。了解http协议有助于我们在抓取数据的时候对一些能够加速抓取速度的参数能够精准的控制,比如说keep-alive等。

    threading模块(多线程):

    我们平时编写的程序都是单线程程序,我们写的代码都在主线程里面运行,这个主线程又运行在python进程中。

    在python中实现多线程是通过一个名字叫做threading的模块来实现。之前还有thread模块,但是threading对于线程的控制更强,因此我们后来都改用threading来实现多线程编程了。

    简单来说,使用threading模块编写多线程程序,就是先自己定义一个类,然后这个类要继承threading.Thread,并且把每个线程要做的工作代码写到一个类的run方法中,当然如果线程本身在创建的时候如果要做一些初始化工作,那么就要在他的__init__方法中编写好初始化工作所要执行的代码,这个方法就像php,java中的构造方法一样。

    这里还要额外讲的一点就是线程安全这个概念。通常情况下我们单线程情况下每个时刻只有一个线程在对资源(文件,变量)操作,所以不可能会出现冲突。但是当多线程的情况下,可能会出现同一个时刻两个线程在操作同一个资源,导致资源损坏,所以我们需要一种机制来解决这种冲突带来的破坏,通常有加锁等操作,比如说mysql数据库的innodb表引擎有行级锁等,文件操作有读取锁等等,这些都是他们的程序底层帮我们完成了。所以我们通常只要知道那些操作,或者那些程序对于线程安全问题做了处理,然后就可以在多线程编程中去使用它们了。而这种考虑到线程安全问题的程序一般就叫做“线程安全版本”,比如说php就有TS版本,这个TS就是Thread Safety线程安全的意思。下面我们要讲到的Queue模块就是一种线程安全的队列数据结构,所以我们可以放心的在多线程编程中使用它。

    最后我们就要来讲讲至关重要的线程阻塞这个概念了。当我们详细学习完threading模块之后,大概就知道如何创建和启动线程了。但是如果我们把线程创建好了,然后调用了start方法,那么我们会发现好像整个程序立马就结束了,这是怎么回事呢?其实这是因为我们在主线程中只有负责启动子线程的代码,也就意味着主线程只有启动子线程的功能,至于子线程执行的那些代码,他们本质上只是写在类里面的一个方法,并没在主线程里面真正去执行他,所以主线程启动完子线程之后他的本职工作就已经全部完成了,已经光荣退场了。既然主线程都退场了,那么python进程就跟着结束了,那么其他线程也就没有内存空间继续执行了。所以我们应该是要让主线程大哥等到所有的子线程小弟全部执行完毕再光荣退场,那么在线程对象中有什么方法能够把主线程卡住呢?thread.sleep嘛?这确实是个办法,但是究竟应该让主线程sleep多久呢?我们并不能准确知道执行完一个任务要多久时间,肯定不能用这个办法。所以我们这个时候应该上网查询一下有什么办法能够让子线程“卡住”主线程呢?“卡住”这个词好像太粗鄙了,其实说专业一点,应该叫做“阻塞”,所以我们可以查询“python 子线程阻塞主线程”,如果我们会正确使用搜索引擎的话,应该会查到一个方法叫做join(),没错,这个join()方法就是子线程用于阻塞主线程的方法,当子线程还未执行完毕的时候,主线程运行到含有join()方法的这一行就会卡在那里,直到所有线程都执行完毕才会执行join()方法后面的代码。

    Queue模块(队列):

    假设有一个这样的场景,我们需要抓取一个人的博客,我们知道这个人的博客有两个页面,一个list.php页面显示的是此博客的所有文章链接,还有一个view.php页面显示的是一篇文章的具体内容。

    如果我们要把这个人的博客里面所有文章内容抓取下来,编写单线程爬虫的思路是:先用正则表达式把这个list.php页面的所有链接a标签的href属性抓取下来,存入一个名字叫做article_list的数组(在python中不叫数组,叫做list,中文名列表),然后再用一个for循环遍历这个article_list数组,用各种抓取网页内容的函数把内容抓取下来然后存入数据库。

    如果我们要编写一个多线程爬虫来完成这个任务的话,就假设我们的程序用10个线程把,那么我们就要想办法把之前抓取的article_list平均分成10份,分别把每一份分配给其中一个子线程。

    但是问题来了,如果我们的article_list数组长度不是10的倍数,也就是文章数量并不是10的整数倍,那么最后一个线程就会比别的线程少分配到一些任务,那么它将会更快的结束。

    如果仅仅是抓取这种只有几千字的博客文章这看似没什么问题,但是如果我们一个任务(不一定是抓取网页的任务,有可能是数学计算,或者图形渲染等等耗时任务)的运行时间很长,那么这将造成极大地资源和时间浪费。我们多线程的目的就是尽可能的利用一切计算资源并且计算时间,所以我们要想办法让任务能够更加科学合理的分配。

    并且我还要考虑一种情况,就是文章数量很大的情况下,我们要既能快速抓取到文章内容,又能尽快的看到我们已经抓取到的内容,这种需求在很多CMS采集站上经常会体现出来。

    比如说我们现在要抓取的目标博客,有几千万篇文章,通常这种情况下博客都会做分页处理,那么我们如果按照上面的传统思路先抓取完list.php的所有页面起码就要几个小时甚至几天,老板如果希望你能够尽快显示出抓取内容,并且尽快将已经抓取到的内容展现到我们的CMS采集站上,那么我们就要实现一边抓取list.php并且把已经抓取到的数据丢入一个article_list数组,一边用另一个线程从article_list数组中提取已经抓取到的文章URL地址,然后这个线程再去对应的URL地址中用正则表达式取到博客文章内容。如何实现这个功能呢?

    我们就需要同时开启两类线程,一类线程专门负责抓取list.php中的url然后丢入article_list数组,另外一类线程专门负责从article_list中提取出url然后从对应的view.php页面中抓取出对应的博客内容。

    但是我们是否还记得前面提到过线程安全这个概念?前一类线程一边往article_list数组中写入数据,另外那一类的线程从article_list中读取数据并且删除已经读取完毕的数据。但是python中list并不是线程安全版本的数据结构,因此这样操作会导致不可预料的错误。所以我们可以尝试使用一个更加方便且线程安全的数据结构,这就是我们的子标题中所提到的Queue队列数据结构。

    同样Queue也有一个join()方法,这个join()方法其实和上一个小节所讲到的threading中join()方法差不多,只不过在Queue中,join()的阻塞条件是当队列不为空空的时候才阻塞,否则继续执行join()后面的代码。在这个爬虫中我便使用了这种方法来阻塞主线程而不是直接通过线程的join方式来阻塞主线程,这样的好处是可以不用写一个死循环来判断当前任务队列中是否还有未执行完的任务,让程序运行更加高效,也让代码更加优雅。

    还有一个细节就是在python2.7中队列模块的名字是Queue,而在python3.x中已经改名为queue,就是首字母大小写的区别,大家如果是复制网上的代码,要记得这个小区别。

    getopt模块:

    如果大家学过c语言的话,对这个模块应该会很熟悉,他就是一个负责从命令行中的命令里面提取出附带参数的模块。比如说我们通常在命令行中操作mysql数据库,就是输入mysql -h127.0.0.1 -uroot -p,其中mysql后面的“-h127.0.0.1 -uroot -p”就是可以获取的参数部分。

    我们平时在编写爬虫的时候,有一些参数是需要用户自己手动输入的,比如说mysql的主机IP,用户名密码等等。为了让我们的程序更加友好通用,有一些配置项是不需要硬编码在代码里面,而是在执行他的时候我们动态传入,结合getopt模块我们就可以实现这个功能。

    hashlib(哈希):

    哈希本质上就是一类数学算法的集合,这种数学算法有个特性就是你给定一个参数,他能够输出另外一个结果,虽然这个结果很短,但是他可以近似认为是独一无二的。比如说我们平时听过的md5,sha-1等等,他们都属于哈希算法。他们可以把一些文件,文字经过一系列的数学运算之后变成短短不到一百位的一段数字英文混合的字符串。

    python中的hashlib模块就为我们封装好了这些数学运算函数,我们只需要简单的调用它就可以完成哈希运算。

    为什么在我这个爬虫中用到了这个包呢?因为在一些接口请求中,服务器需要带上一些校验码,保证接口请求的数据没有被篡改或者丢失,这些校验码一般都是hash算法,所以我们需要用到这个模块来完成这种运算。

    json:

    很多时候我们抓取到的数据不是html,而是一些json数据,json本质上只是一段含有键值对的字符串,如果我们需要提取出其中特定的字符串,那么我们需要json这个模块来将这个json字符串转换为dict类型方便我们操作。

    re(正则表达式):

    有的时候我们抓取到了一些网页内容,但是我们需要将网页中的一些特定格式的内容提取出来,比如说电子邮箱的格式一般都是前面几位英文数字字母加一个@符号加http://xxx.xxx的域名,而要像计算机语言描述这种格式,我们可以使用一种叫做正则表达式的表达式来表达出这种格式,并且让计算机自动从一大段字符串中将符合这种特定格式的文字匹配出来。

    sys:

    这个模块主要用于处理一些系统方面的事情,在这个爬虫中我用他来解决输出编码问题。

    time:

    稍微学过一点英语的人都能够猜出来这个模块用于处理时间,在这个爬虫中我用它来获取当前时间戳,然后通过在主线程末尾用当前时间戳减去程序开始运行时的时间戳,得到程序的运行时间。

    如图所示,开50个线程抓取100页(每页30个帖子,相当于抓取了3000个帖子)贴吧帖子内容并且从中提取出手机邮箱这个步骤共耗时330秒。

    urllib和urllib2:

    这两个模块都是用于处理一些http请求,以及url格式化方面的事情。我的爬虫http请求部分的核心代码就是使用这个模块完成的。

    MySQLdb:

    这是一个第三方模块,用于在python中操作mysql数据库。

    这里我们要注意一个细节问题:mysqldb模块并不是线程安全版本,意味着我们不能在多线程中共享同一个mysql连接句柄。所以大家可以在我的代码中看到,我在每个线程的构造函数中都传入了一个新的mysql连接句柄。因此每个子线程只会用自己独立的mysql连接句柄。

    cmd_color_printers:

    这也是一个第三方模块,网上能够找到相关代码,这个模块主要用于向命令行中输出彩色字符串。比如说我们通常爬虫出现错误,要输出红色的字体会比较显眼,就要使用到这个模块。

    自动化爬虫的错误处理:

    如果大家在网络质量不是很好的环境下使用该爬虫,会发现有的时候会报如图所示的异常,这是我为了偷懒并没有写各种异常处理的逻辑。

    通常情况下我们如果要编写高度自动化的爬虫,那么就需要预料到我们的爬虫可能会遇到的所有异常情况,针对这些异常情况做处理。

    比如说如图所示的错误,我们就应该把当时正在处理的任务重新塞入任务队列,否则我们就会出现遗漏信息的情况。这也是爬虫编写的一个复杂点。

    总结:

    其实多线程爬虫的编写也不复杂,多看示例代码,多自己动手尝试,多去社区,论坛交流,很多经典的书上对多线程编程也有非常详细的解释。这篇文章本质上主要还是一篇科普文章,内容讲解的都不是很深入,大家还需要课外自己多结合网上各种资料自己学习。返回搜狐,查看更多

    责任编辑:

    展开全文
  • 知道大家过年都是怎么过的,反正栏主是在家睡了一天,醒来的时候登QQ发现有人找我要一份贴吧爬虫的源代码,想起之前练手的时候写过一个抓取百度贴吧发帖记录中的邮箱与手机的爬...
        

    不知道大家过年都是怎么过的,反正栏主是在家睡了一天,醒来的时候登QQ发现有人找我要一份贴吧爬虫的源代码,想起之前练手的时候写过一个抓取百度贴吧发帖记录中的邮箱与手机号的爬虫,于是开源分享给大家学习与参考。

    需求分析:

    本爬虫主要是对百度贴吧中各种帖子的内容进行抓取,并且分析帖子内容将其中的手机号和邮箱地址抓取出来。主要流程在代码注释中有详细解释。

    测试环境:

    代码在Windows7 64bit,python 2.7 64bit(安装mysqldb扩展)以及centos 6.5,python 2.7(带mysqldb扩展)环境下测试通过

    0?wx_fmt=png

    0?wx_fmt=png

    环境准备:

    工欲善其事必先利其器,大家可以从截图看出我的环境是Windows 7 + PyCharm。我的Python环境是Python 2.7 64bit。这是比较适合新手使用的开发环境。然后我再建议大家安装一个easy_install,听名字就知道这是一个安装器,它是用来安装一些扩展包的,比如说在python中如果我们要操作mysql数据库的话,python原生是不支持的,我们必须安装mysqldb包来让python可以操作mysql数据库,如果有easy_install的话我们只需要一行命令就可以快速安装号mysqldb扩展包,他就像php中的composer,centos中的yum,Ubuntu中的apt-get一样方便。

    相关工具可在我的github中找到:cw1997/python-tools,其中easy_install的安装只需要在python命令行下运行那个py脚本然后稍等片刻即可,他会自动加入Windows的环境变量,在Windows命令行下如果输入easy_install有回显说明安装成功。

    环境选择的细节说明:

    至于电脑硬件当然是越快越好,内存起码8G起步,因为爬虫本身需要大量存储和解析中间数据,尤其是多线程爬虫,在碰到抓取带有分页的列表和详情页,并且抓取数据量很大的情况下使用queue队列分配抓取任务会非常占内存。包括有的时候我们抓取的数据是使用json,如果使用mongodb等nosql数据库存储,也会很占内存。

    网络连接建议使用有线网,因为市面上一些劣质的无线路由器和普通的民用无线网卡在线程开的比较大的情况下会出现间歇性断网或者数据丢失,掉包等情况,这个我亲有体会。

    至于操作系统和python当然肯定是选择64位。如果你使用的是32位的操作系统,那么无法使用大内存。如果你使用的是32位的python,可能在小规模抓取数据的时候感觉不出有什么问题,但是当数据量变大的时候,比如说某个列表,队列,字典里面存储了大量数据,导致python的内存占用超过2g的时候会报内存溢出错误。原因在我曾经segmentfault上提过的问题中依云的回答有解释(java – python只要占用内存达到1.9G之后httplib模块就开始报内存溢出错误 – SegmentFault

    如果你准备使用mysql存储数据,建议使用mysql5.5以后的版本,因为mysql5.5版本支持json数据类型,这样的话可以抛弃mongodb了。(有人说mysql会比mongodb稳定一点,这个我不确定。)

    至于现在python都已经出了3.x版本了,为什么我这里还使用的是python2.7?我个人选择2.7版本的原因是自己当初很早以前买的python核心编程这本书是第二版的,仍然以2.7为示例版本。并且目前网上仍然有大量的教程资料是以2.7为版本讲解,2.7在某些方面与3.x还是有很大差别,如果我们没有学过2.7,可能对于一些细微的语法差别不是很懂会导致我们理解上出现偏差,或者看不懂demo代码。而且现在还是有部分依赖包只兼容2.7版本。我的建议是如果你是准备急着学python然后去公司工作,并且公司没有老代码需要维护,那么可以考虑直接上手3.x,如果你有比较充裕的时间,并且没有很系统的大牛带,只能依靠网上零零散散的博客文章来学习,那么还是先学2.7在学3.x,毕竟学会了2.7之后3.x上手也很快。

    多线程爬虫涉及到的知识点:

    其实对于任何软件项目而言,我们凡是想知道编写这个项目需要什么知识点,我们都可以观察一下这个项目的主要入口文件都导入了哪些包。

    0?wx_fmt=png

    现在来看一下我们这个项目,作为一个刚接触python的人,可能有一些包几乎都没有用过,那么我们在本小节就来简单的说说这些包起什么作用,要掌握他们分别会涉及到什么知识点,这些知识点的关键词是什么。这篇文章并不会花费长篇大论来从基础讲起,因此我们要学会善用百度,搜索这些知识点的关键词来自学。下面就来一一分析一下这些知识点。

    HTTP协议:

    我们的爬虫抓取数据本质上就是不停的发起http请求,获取http响应,将其存入我们的电脑中。了解http协议有助于我们在抓取数据的时候对一些能够加速抓取速度的参数能够精准的控制,比如说keep-alive等。

    threading模块(多线程):

    我们平时编写的程序都是单线程程序,我们写的代码都在主线程里面运行,这个主线程又运行在python进程中。关于线程和进程的解释可以参考阮一峰的博客:进程与线程的一个简单解释 – 阮一峰的网络日志

    在python中实现多线程是通过一个名字叫做threading的模块来实现。之前还有thread模块,但是threading对于线程的控制更强,因此我们后来都改用threading来实现多线程编程了。

    关于threading多线程的一些用法,我觉得这篇文章不错:[python] 专题八.多线程编程之thread和threading 大家可以参考参考。

    简单来说,使用threading模块编写多线程程序,就是先自己定义一个类,然后这个类要继承threading.Thread,并且把每个线程要做的工作代码写到一个类的run方法中,当然如果线程本身在创建的时候如果要做一些初始化工作,那么就要在他的__init__方法中编写好初始化工作所要执行的代码,这个方法就像php,java中的构造方法一样。

    这里还要额外讲的一点就是线程安全这个概念。通常情况下我们单线程情况下每个时刻只有一个线程在对资源(文件,变量)操作,所以不可能会出现冲突。但是当多线程的情况下,可能会出现同一个时刻两个线程在操作同一个资源,导致资源损坏,所以我们需要一种机制来解决这种冲突带来的破坏,通常有加锁等操作,比如说mysql数据库的innodb表引擎有行级锁等,文件操作有读取锁等等,这些都是他们的程序底层帮我们完成了。所以我们通常只要知道那些操作,或者那些程序对于线程安全问题做了处理,然后就可以在多线程编程中去使用它们了。而这种考虑到线程安全问题的程序一般就叫做“线程安全版本”,比如说php就有TS版本,这个TS就是Thread Safety线程安全的意思。下面我们要讲到的Queue模块就是一种线程安全的队列数据结构,所以我们可以放心的在多线程编程中使用它。

    最后我们就要来讲讲至关重要的线程阻塞这个概念了。当我们详细学习完threading模块之后,大概就知道如何创建和启动线程了。但是如果我们把线程创建好了,然后调用了start方法,那么我们会发现好像整个程序立马就结束了,这是怎么回事呢?其实这是因为我们在主线程中只有负责启动子线程的代码,也就意味着主线程只有启动子线程的功能,至于子线程执行的那些代码,他们本质上只是写在类里面的一个方法,并没在主线程里面真正去执行他,所以主线程启动完子线程之后他的本职工作就已经全部完成了,已经光荣退场了。既然主线程都退场了,那么python进程就跟着结束了,那么其他线程也就没有内存空间继续执行了。所以我们应该是要让主线程大哥等到所有的子线程小弟全部执行完毕再光荣退场,那么在线程对象中有什么方法能够把主线程卡住呢?thread.sleep嘛?这确实是个办法,但是究竟应该让主线程sleep多久呢?我们并不能准确知道执行完一个任务要多久时间,肯定不能用这个办法。所以我们这个时候应该上网查询一下有什么办法能够让子线程“卡住”主线程呢?“卡住”这个词好像太粗鄙了,其实说专业一点,应该叫做“阻塞”,所以我们可以查询“python 子线程阻塞主线程”,如果我们会正确使用搜索引擎的话,应该会查到一个方法叫做join(),没错,这个join()方法就是子线程用于阻塞主线程的方法,当子线程还未执行完毕的时候,主线程运行到含有join()方法的这一行就会卡在那里,直到所有线程都执行完毕才会执行join()方法后面的代码。

    Queue模块(队列):

    假设有一个这样的场景,我们需要抓取一个人的博客,我们知道这个人的博客有两个页面,一个list.php页面显示的是此博客的所有文章链接,还有一个view.php页面显示的是一篇文章的具体内容。

    如果我们要把这个人的博客里面所有文章内容抓取下来,编写单线程爬虫的思路是:先用正则表达式把这个list.php页面的所有链接a标签的href属性抓取下来,存入一个名字叫做article_list的数组(在python中不叫数组,叫做list,中文名列表),然后再用一个for循环遍历这个article_list数组,用各种抓取网页内容的函数把内容抓取下来然后存入数据库。

    如果我们要编写一个多线程爬虫来完成这个任务的话,就假设我们的程序用10个线程把,那么我们就要想办法把之前抓取的article_list平均分成10份,分别把每一份分配给其中一个子线程。

    但是问题来了,如果我们的article_list数组长度不是10的倍数,也就是文章数量并不是10的整数倍,那么最后一个线程就会比别的线程少分配到一些任务,那么它将会更快的结束。

    如果仅仅是抓取这种只有几千字的博客文章这看似没什么问题,但是如果我们一个任务(不一定是抓取网页的任务,有可能是数学计算,或者图形渲染等等耗时任务)的运行时间很长,那么这将造成极大地资源和时间浪费。我们多线程的目的就是尽可能的利用一切计算资源并且计算时间,所以我们要想办法让任务能够更加科学合理的分配。

    并且我还要考虑一种情况,就是文章数量很大的情况下,我们要既能快速抓取到文章内容,又能尽快的看到我们已经抓取到的内容,这种需求在很多CMS采集站上经常会体现出来。

    比如说我们现在要抓取的目标博客,有几千万篇文章,通常这种情况下博客都会做分页处理,那么我们如果按照上面的传统思路先抓取完list.php的所有页面起码就要几个小时甚至几天,老板如果希望你能够尽快显示出抓取内容,并且尽快将已经抓取到的内容展现到我们的CMS采集站上,那么我们就要实现一边抓取list.php并且把已经抓取到的数据丢入一个article_list数组,一边用另一个线程从article_list数组中提取已经抓取到的文章URL地址,然后这个线程再去对应的URL地址中用正则表达式取到博客文章内容。如何实现这个功能呢?

    我们就需要同时开启两类线程,一类线程专门负责抓取list.php中的url然后丢入article_list数组,另外一类线程专门负责从article_list中提取出url然后从对应的view.php页面中抓取出对应的博客内容。

    但是我们是否还记得前面提到过线程安全这个概念?前一类线程一边往article_list数组中写入数据,另外那一类的线程从article_list中读取数据并且删除已经读取完毕的数据。但是python中list并不是线程安全版本的数据结构,因此这样操作会导致不可预料的错误。所以我们可以尝试使用一个更加方便且线程安全的数据结构,这就是我们的子标题中所提到的Queue队列数据结构。

    同样Queue也有一个join()方法,这个join()方法其实和上一个小节所讲到的threading中join()方法差不多,只不过在Queue中,join()的阻塞条件是当队列不为空空的时候才阻塞,否则继续执行join()后面的代码。在这个爬虫中我便使用了这种方法来阻塞主线程而不是直接通过线程的join方式来阻塞主线程,这样的好处是可以不用写一个死循环来判断当前任务队列中是否还有未执行完的任务,让程序运行更加高效,也让代码更加优雅。

    还有一个细节就是在python2.7中队列模块的名字是Queue,而在python3.x中已经改名为queue,就是首字母大小写的区别,大家如果是复制网上的代码,要记得这个小区别。

    getopt模块:

    如果大家学过c语言的话,对这个模块应该会很熟悉,他就是一个负责从命令行中的命令里面提取出附带参数的模块。比如说我们通常在命令行中操作mysql数据库,就是输入mysql -h127.0.0.1 -uroot -p,其中mysql后面的“-h127.0.0.1 -uroot -p”就是可以获取的参数部分。

    我们平时在编写爬虫的时候,有一些参数是需要用户自己手动输入的,比如说mysql的主机IP,用户名密码等等。为了让我们的程序更加友好通用,有一些配置项是不需要硬编码在代码里面,而是在执行他的时候我们动态传入,结合getopt模块我们就可以实现这个功能。

    hashlib(哈希):

    哈希本质上就是一类数学算法的集合,这种数学算法有个特性就是你给定一个参数,他能够输出另外一个结果,虽然这个结果很短,但是他可以近似认为是独一无二的。比如说我们平时听过的md5,sha-1等等,他们都属于哈希算法。他们可以把一些文件,文字经过一系列的数学运算之后变成短短不到一百位的一段数字英文混合的字符串。

    python中的hashlib模块就为我们封装好了这些数学运算函数,我们只需要简单的调用它就可以完成哈希运算。

    为什么在我这个爬虫中用到了这个包呢?因为在一些接口请求中,服务器需要带上一些校验码,保证接口请求的数据没有被篡改或者丢失,这些校验码一般都是hash算法,所以我们需要用到这个模块来完成这种运算。

    json:

    很多时候我们抓取到的数据不是html,而是一些json数据,json本质上只是一段含有键值对的字符串,如果我们需要提取出其中特定的字符串,那么我们需要json这个模块来将这个json字符串转换为dict类型方便我们操作。

    re(正则表达式):

    有的时候我们抓取到了一些网页内容,但是我们需要将网页中的一些特定格式的内容提取出来,比如说电子邮箱的格式一般都是前面几位英文数字字母加一个@符号加http://xxx.xxx的域名,而要像计算机语言描述这种格式,我们可以使用一种叫做正则表达式的表达式来表达出这种格式,并且让计算机自动从一大段字符串中将符合这种特定格式的文字匹配出来。

    sys:

    这个模块主要用于处理一些系统方面的事情,在这个爬虫中我用他来解决输出编码问题。

    time:

    稍微学过一点英语的人都能够猜出来这个模块用于处理时间,在这个爬虫中我用它来获取当前时间戳,然后通过在主线程末尾用当前时间戳减去程序开始运行时的时间戳,得到程序的运行时间。

    0?wx_fmt=png

    如图所示,开50个线程抓取100页(每页30个帖子,相当于抓取了3000个帖子)贴吧帖子内容并且从中提取出手机邮箱这个步骤共耗时330秒。

    urllib和urllib2:

    这两个模块都是用于处理一些http请求,以及url格式化方面的事情。我的爬虫http请求部分的核心代码就是使用这个模块完成的。

    MySQLdb:

    这是一个第三方模块,用于在python中操作mysql数据库。

    这里我们要注意一个细节问题:mysqldb模块并不是线程安全版本,意味着我们不能在多线程中共享同一个mysql连接句柄。所以大家可以在我的代码中看到,我在每个线程的构造函数中都传入了一个新的mysql连接句柄。因此每个子线程只会用自己独立的mysql连接句柄。

    cmd_color_printers:

    这也是一个第三方模块,网上能够找到相关代码,这个模块主要用于向命令行中输出彩色字符串。比如说我们通常爬虫出现错误,要输出红色的字体会比较显眼,就要使用到这个模块。

    自动化爬虫的错误处理:

    0?wx_fmt=png

    如果大家在网络质量不是很好的环境下使用该爬虫,会发现有的时候会报如图所示的异常,这是我为了偷懒并没有写各种异常处理的逻辑。

    通常情况下我们如果要编写高度自动化的爬虫,那么就需要预料到我们的爬虫可能会遇到的所有异常情况,针对这些异常情况做处理。

    比如说如图所示的错误,我们就应该把当时正在处理的任务重新塞入任务队列,否则我们就会出现遗漏信息的情况。这也是爬虫编写的一个复杂点。

    总结:

    其实多线程爬虫的编写也不复杂,多看示例代码,多自己动手尝试,多去社区,论坛交流,很多经典的书上对多线程编程也有非常详细的解释。这篇文章本质上主要还是一篇科普文章,内容讲解的都不是很深入,大家还需要课外自己多结合网上各种资料自己学习。

    展开全文
  • 知道大家过年都是怎么过的,反正栏主是在家睡了一天,醒来的时候登QQ发现有人找我要一份贴吧爬虫的源代码,想起之前练手的时候写过一个抓取百度贴吧发帖记录中的邮箱与手机的爬虫,于是开源分享给大家学习与参考...

    不知道大家过年都是怎么过的,反正栏主是在家睡了一天,醒来的时候登QQ发现有人找我要一份贴吧爬虫的源代码,想起之前练手的时候写过一个抓取百度贴吧发帖记录中的邮箱与手机号的爬虫,于是开源分享给大家学习与参考。

    需求分析:

    本爬虫主要是对百度贴吧中各种帖子的内容进行抓取,并且分析帖子内容将其中的手机号和邮箱地址抓取出来。主要流程在代码注释中有详细解释。

    测试环境:

    代码在Windows7 64bit,python 2.7 64bit(安装mysqldb扩展)以及centos 6.5,python 2.7(带mysqldb扩展)环境下测试通过

    环境准备:

    工欲善其事必先利其器,大家可以从截图看出我的环境是Windows 7 + PyCharm。我的Python环境是Python 2.7 64bit。这是比较适合新手使用的开发环境。然后我再建议大家安装一个easy_install,听名字就知道这是一个安装器,它是用来安装一些扩展包的,比如说在python中如果我们要操作mysql数据库的话,python原生是不支持的,我们必须安装mysqldb包来让python可以操作mysql数据库,如果有easy_install的话我们只需要一行命令就可以快速安装号mysqldb扩展包,他就像php中的composer,centos中的yum,Ubuntu中的apt-get一样方便。

    相关工具可在我的github中找到:cw1997/python-tools,其中easy_install的安装只需要在python命令行下运行那个py脚本然后稍等片刻即可,他会自动加入Windows的环境变量,在Windows命令行下如果输入easy_install有回显说明安装成功。

    环境选择的细节说明:

    至于电脑硬件当然是越快越好,内存起码8G起步,因为爬虫本身需要大量存储和解析中间数据,尤其是多线程爬虫,在碰到抓取带有分页的列表和详情页,并且抓取数据量很大的情况下使用queue队列分配抓取任务会非常占内存。包括有的时候我们抓取的数据是使用json,如果使用mongodb等nosql数据库存储,也会很占内存。

    网络连接建议使用有线网,因为市面上一些劣质的无线路由器和普通的民用无线网卡在线程开的比较大的情况下会出现间歇性断网或者数据丢失,掉包等情况,这个我亲有体会。

    至于操作系统和python当然肯定是选择64位。如果你使用的是32位的操作系统,那么无法使用大内存。如果你使用的是32位的python,可能在小规模抓取数据的时候感觉不出有什么问题,但是当数据量变大的时候,比如说某个列表,队列,字典里面存储了大量数据,导致python的内存占用超过2g的时候会报内存溢出错误。原因在我曾经segmentfault上提过的问题中依云的回答有解释(java – python只要占用内存达到1.9G之后httplib模块就开始报内存溢出错误 – SegmentFault

    如果你准备使用mysql存储数据,建议使用mysql5.5以后的版本,因为mysql5.5版本支持json数据类型,这样的话可以抛弃mongodb了。(有人说mysql会比mongodb稳定一点,这个我不确定。)

    至于现在python都已经出了3.x版本了,为什么我这里还使用的是python2.7?我个人选择2.7版本的原因是自己当初很早以前买的python核心编程这本书是第二版的,仍然以2.7为示例版本。并且目前网上仍然有大量的教程资料是以2.7为版本讲解,2.7在某些方面与3.x还是有很大差别,如果我们没有学过2.7,可能对于一些细微的语法差别不是很懂会导致我们理解上出现偏差,或者看不懂demo代码。而且现在还是有部分依赖包只兼容2.7版本。我的建议是如果你是准备急着学python然后去公司工作,并且公司没有老代码需要维护,那么可以考虑直接上手3.x,如果你有比较充裕的时间,并且没有很系统的大牛带,只能依靠网上零零散散的博客文章来学习,那么还是先学2.7在学3.x,毕竟学会了2.7之后3.x上手也很快。

    多线程爬虫涉及到的知识点:

    其实对于任何软件项目而言,我们凡是想知道编写这个项目需要什么知识点,我们都可以观察一下这个项目的主要入口文件都导入了哪些包。

    现在来看一下我们这个项目,作为一个刚接触python的人,可能有一些包几乎都没有用过,那么我们在本小节就来简单的说说这些包起什么作用,要掌握他们分别会涉及到什么知识点,这些知识点的关键词是什么。这篇文章并不会花费长篇大论来从基础讲起,因此我们要学会善用百度,搜索这些知识点的关键词来自学。下面就来一一分析一下这些知识点。

    HTTP协议:

    我们的爬虫抓取数据本质上就是不停的发起http请求,获取http响应,将其存入我们的电脑中。了解http协议有助于我们在抓取数据的时候对一些能够加速抓取速度的参数能够精准的控制,比如说keep-alive等。

    threading模块(多线程):

    我们平时编写的程序都是单线程程序,我们写的代码都在主线程里面运行,这个主线程又运行在python进程中。关于线程和进程的解释可以参考阮一峰的博客:进程与线程的一个简单解释 – 阮一峰的网络日志

    在python中实现多线程是通过一个名字叫做threading的模块来实现。之前还有thread模块,但是threading对于线程的控制更强,因此我们后来都改用threading来实现多线程编程了。

    关于threading多线程的一些用法,我觉得这篇文章不错:[python] 专题八.多线程编程之thread和threading 大家可以参考参考。

    简单来说,使用threading模块编写多线程程序,就是先自己定义一个类,然后这个类要继承threading.Thread,并且把每个线程要做的工作代码写到一个类的run方法中,当然如果线程本身在创建的时候如果要做一些初始化工作,那么就要在他的__init__方法中编写好初始化工作所要执行的代码,这个方法就像php,java中的构造方法一样。

    这里还要额外讲的一点就是线程安全这个概念。通常情况下我们单线程情况下每个时刻只有一个线程在对资源(文件,变量)操作,所以不可能会出现冲突。但是当多线程的情况下,可能会出现同一个时刻两个线程在操作同一个资源,导致资源损坏,所以我们需要一种机制来解决这种冲突带来的破坏,通常有加锁等操作,比如说mysql数据库的innodb表引擎有行级锁等,文件操作有读取锁等等,这些都是他们的程序底层帮我们完成了。所以我们通常只要知道那些操作,或者那些程序对于线程安全问题做了处理,然后就可以在多线程编程中去使用它们了。而这种考虑到线程安全问题的程序一般就叫做“线程安全版本”,比如说php就有TS版本,这个TS就是Thread Safety线程安全的意思。下面我们要讲到的Queue模块就是一种线程安全的队列数据结构,所以我们可以放心的在多线程编程中使用它。

    最后我们就要来讲讲至关重要的线程阻塞这个概念了。当我们详细学习完threading模块之后,大概就知道如何创建和启动线程了。但是如果我们把线程创建好了,然后调用了start方法,那么我们会发现好像整个程序立马就结束了,这是怎么回事呢?其实这是因为我们在主线程中只有负责启动子线程的代码,也就意味着主线程只有启动子线程的功能,至于子线程执行的那些代码,他们本质上只是写在类里面的一个方法,并没在主线程里面真正去执行他,所以主线程启动完子线程之后他的本职工作就已经全部完成了,已经光荣退场了。既然主线程都退场了,那么python进程就跟着结束了,那么其他线程也就没有内存空间继续执行了。所以我们应该是要让主线程大哥等到所有的子线程小弟全部执行完毕再光荣退场,那么在线程对象中有什么方法能够把主线程卡住呢?thread.sleep嘛?这确实是个办法,但是究竟应该让主线程sleep多久呢?我们并不能准确知道执行完一个任务要多久时间,肯定不能用这个办法。所以我们这个时候应该上网查询一下有什么办法能够让子线程“卡住”主线程呢?“卡住”这个词好像太粗鄙了,其实说专业一点,应该叫做“阻塞”,所以我们可以查询“python 子线程阻塞主线程”,如果我们会正确使用搜索引擎的话,应该会查到一个方法叫做join(),没错,这个join()方法就是子线程用于阻塞主线程的方法,当子线程还未执行完毕的时候,主线程运行到含有join()方法的这一行就会卡在那里,直到所有线程都执行完毕才会执行join()方法后面的代码。

    Queue模块(队列):

    假设有一个这样的场景,我们需要抓取一个人的博客,我们知道这个人的博客有两个页面,一个list.php页面显示的是此博客的所有文章链接,还有一个view.php页面显示的是一篇文章的具体内容。

    如果我们要把这个人的博客里面所有文章内容抓取下来,编写单线程爬虫的思路是:先用正则表达式把这个list.php页面的所有链接a标签的href属性抓取下来,存入一个名字叫做article_list的数组(在python中不叫数组,叫做list,中文名列表),然后再用一个for循环遍历这个article_list数组,用各种抓取网页内容的函数把内容抓取下来然后存入数据库。

    如果我们要编写一个多线程爬虫来完成这个任务的话,就假设我们的程序用10个线程把,那么我们就要想办法把之前抓取的article_list平均分成10份,分别把每一份分配给其中一个子线程。

    但是问题来了,如果我们的article_list数组长度不是10的倍数,也就是文章数量并不是10的整数倍,那么最后一个线程就会比别的线程少分配到一些任务,那么它将会更快的结束。

    如果仅仅是抓取这种只有几千字的博客文章这看似没什么问题,但是如果我们一个任务(不一定是抓取网页的任务,有可能是数学计算,或者图形渲染等等耗时任务)的运行时间很长,那么这将造成极大地资源和时间浪费。我们多线程的目的就是尽可能的利用一切计算资源并且计算时间,所以我们要想办法让任务能够更加科学合理的分配。

    并且我还要考虑一种情况,就是文章数量很大的情况下,我们要既能快速抓取到文章内容,又能尽快的看到我们已经抓取到的内容,这种需求在很多CMS采集站上经常会体现出来。

    比如说我们现在要抓取的目标博客,有几千万篇文章,通常这种情况下博客都会做分页处理,那么我们如果按照上面的传统思路先抓取完list.php的所有页面起码就要几个小时甚至几天,老板如果希望你能够尽快显示出抓取内容,并且尽快将已经抓取到的内容展现到我们的CMS采集站上,那么我们就要实现一边抓取list.php并且把已经抓取到的数据丢入一个article_list数组,一边用另一个线程从article_list数组中提取已经抓取到的文章URL地址,然后这个线程再去对应的URL地址中用正则表达式取到博客文章内容。如何实现这个功能呢?

    我们就需要同时开启两类线程,一类线程专门负责抓取list.php中的url然后丢入article_list数组,另外一类线程专门负责从article_list中提取出url然后从对应的view.php页面中抓取出对应的博客内容。

    但是我们是否还记得前面提到过线程安全这个概念?前一类线程一边往article_list数组中写入数据,另外那一类的线程从article_list中读取数据并且删除已经读取完毕的数据。但是python中list并不是线程安全版本的数据结构,因此这样操作会导致不可预料的错误。所以我们可以尝试使用一个更加方便且线程安全的数据结构,这就是我们的子标题中所提到的Queue队列数据结构。

    同样Queue也有一个join()方法,这个join()方法其实和上一个小节所讲到的threading中join()方法差不多,只不过在Queue中,join()的阻塞条件是当队列不为空空的时候才阻塞,否则继续执行join()后面的代码。在这个爬虫中我便使用了这种方法来阻塞主线程而不是直接通过线程的join方式来阻塞主线程,这样的好处是可以不用写一个死循环来判断当前任务队列中是否还有未执行完的任务,让程序运行更加高效,也让代码更加优雅。

    还有一个细节就是在python2.7中队列模块的名字是Queue,而在python3.x中已经改名为queue,就是首字母大小写的区别,大家如果是复制网上的代码,要记得这个小区别。

    getopt模块:

    如果大家学过c语言的话,对这个模块应该会很熟悉,他就是一个负责从命令行中的命令里面提取出附带参数的模块。比如说我们通常在命令行中操作mysql数据库,就是输入mysql -h127.0.0.1 -uroot -p,其中mysql后面的“-h127.0.0.1 -uroot -p”就是可以获取的参数部分。

    我们平时在编写爬虫的时候,有一些参数是需要用户自己手动输入的,比如说mysql的主机IP,用户名密码等等。为了让我们的程序更加友好通用,有一些配置项是不需要硬编码在代码里面,而是在执行他的时候我们动态传入,结合getopt模块我们就可以实现这个功能。

    hashlib(哈希):

    哈希本质上就是一类数学算法的集合,这种数学算法有个特性就是你给定一个参数,他能够输出另外一个结果,虽然这个结果很短,但是他可以近似认为是独一无二的。比如说我们平时听过的md5,sha-1等等,他们都属于哈希算法。他们可以把一些文件,文字经过一系列的数学运算之后变成短短不到一百位的一段数字英文混合的字符串。

    python中的hashlib模块就为我们封装好了这些数学运算函数,我们只需要简单的调用它就可以完成哈希运算。

    为什么在我这个爬虫中用到了这个包呢?因为在一些接口请求中,服务器需要带上一些校验码,保证接口请求的数据没有被篡改或者丢失,这些校验码一般都是hash算法,所以我们需要用到这个模块来完成这种运算。

    json:

    很多时候我们抓取到的数据不是html,而是一些json数据,json本质上只是一段含有键值对的字符串,如果我们需要提取出其中特定的字符串,那么我们需要json这个模块来将这个json字符串转换为dict类型方便我们操作。

    re(正则表达式):

    有的时候我们抓取到了一些网页内容,但是我们需要将网页中的一些特定格式的内容提取出来,比如说电子邮箱的格式一般都是前面几位英文数字字母加一个@符号加http://xxx.xxx的域名,而要像计算机语言描述这种格式,我们可以使用一种叫做正则表达式的表达式来表达出这种格式,并且让计算机自动从一大段字符串中将符合这种特定格式的文字匹配出来。

    sys:

    这个模块主要用于处理一些系统方面的事情,在这个爬虫中我用他来解决输出编码问题。

    time:

    稍微学过一点英语的人都能够猜出来这个模块用于处理时间,在这个爬虫中我用它来获取当前时间戳,然后通过在主线程末尾用当前时间戳减去程序开始运行时的时间戳,得到程序的运行时间。

    如图所示,开50个线程抓取100页(每页30个帖子,相当于抓取了3000个帖子)贴吧帖子内容并且从中提取出手机邮箱这个步骤共耗时330秒。

    urllib和urllib2:

    这两个模块都是用于处理一些http请求,以及url格式化方面的事情。我的爬虫http请求部分的核心代码就是使用这个模块完成的。

    MySQLdb:

    这是一个第三方模块,用于在python中操作mysql数据库。

    这里我们要注意一个细节问题:mysqldb模块并不是线程安全版本,意味着我们不能在多线程中共享同一个mysql连接句柄。所以大家可以在我的代码中看到,我在每个线程的构造函数中都传入了一个新的mysql连接句柄。因此每个子线程只会用自己独立的mysql连接句柄。

    cmd_color_printers:

    这也是一个第三方模块,网上能够找到相关代码,这个模块主要用于向命令行中输出彩色字符串。比如说我们通常爬虫出现错误,要输出红色的字体会比较显眼,就要使用到这个模块。

    自动化爬虫的错误处理:

    如果大家在网络质量不是很好的环境下使用该爬虫,会发现有的时候会报如图所示的异常,这是我为了偷懒并没有写各种异常处理的逻辑。

    通常情况下我们如果要编写高度自动化的爬虫,那么就需要预料到我们的爬虫可能会遇到的所有异常情况,针对这些异常情况做处理。

    比如说如图所示的错误,我们就应该把当时正在处理的任务重新塞入任务队列,否则我们就会出现遗漏信息的情况。这也是爬虫编写的一个复杂点。

    总结:

    其实多线程爬虫的编写也不复杂,多看示例代码,多自己动手尝试,多去社区,论坛交流,很多经典的书上对多线程编程也有非常详细的解释。这篇文章本质上主要还是一篇科普文章,内容讲解的都不是很深入,大家还需要课外自己多结合网上各种资料自己学习。


    来源:51CTO

    展开全文
  • 每次要发工资条公司负现工资条的同事都很烦恼,二百多号人,只能一个个人去发.于是她问我有什么办法可以批量发工资条的,我说你要收集员工的邮箱地址.然后我花半天写一个工具给你,她说早知道之前就你啦.下面费话不多...

    每次要发工资条公司负现工资条的同事都很烦恼,二百多号人,只能一个个人去发.于是她问我有什么办法可以批量发工资条的,我说你要收集员工的邮箱地址.然后我花半天写一个工具给你,她说早知道之前就找你啦.

    下面费话不多说,直接上代码.

    以下需要安装pandas库以及依赖库.

    如有不懂配置或其它问题可以发邮件给我大家交流一下,我的qq邮箱:447282087import os,datetime,time

    import pandas as pd

    import tkinter as tk

    from tkinter import filedialog

    import smtplib

    from email.mime.text import MIMEText

    from email.utils import formataddr

    def mail(uname,m,txt,tomail,upass):

    my_sender='test126.com'    # 发件人邮箱账号

    my_pass = upass    # 发件人邮箱密码(当时申请smtp给的口令)

    my_user = tomail     # 收件人邮箱账号,我这边发送给自己

    ret=True

    try:

    msg=MIMEText(txt,'html','utf-8')

    msg['From']=formataddr(["发件人昵称",my_sender])  # 括号里的对应发件人邮箱昵称、发件人邮箱账号

    msg['To']=formataddr(["收件人昵称",my_user])              # 括号里的对应收件人邮箱昵称、收件人邮箱账号

    msg['Subject']="%s %s月份工资条" %(uname,m)                # 邮件的主题,也可以说是标题

    server=smtplib.SMTP_SSL("smtp.126.com", 465)  # 发件人邮箱中的SMTP服务器,端口是465

    server.login(my_sender, my_pass)  # 括号中对应的是发件人邮箱账号、邮箱密码

    server.sendmail(my_sender,[my_user,],msg.as_string())  # 括号中对应的是发件人邮箱账号、收件人邮箱账号、发送邮件

    server.quit()# 关闭连接

    except Exception:# 如果 try 中的语句没有执行,则会执行下面的 ret=False

    ret=False

    return ret

    def ReadFile(filePath):

    print('打开文件:%s' % filePath)

    filePath = filePath.replace('\\', '/')

    if (os.path.exists(filePath) == False):

    print('excel文件:%s  不存在  %s' % (filePath, datetime.datetime.now()))

    # dfxsd = pd.read_excel(filePath)

    df = pd.read_excel(filePath, dtype=str)

    err_i = 0

    count_i = 0

    err_name = ''

    m = input('请输入工资条的月份:')

    upass = input('请输入你的邮箱密码:')

    title = ['编号', '姓名', '应出勤天数', '实际出勤(天)', '基本底薪', '实际底薪', '绩效提成', '外访补贴',

    '其他补贴', '加班补贴(天数)', '加班补贴(金额)', '应发工资合计', '事假(天)', '事假(金额)',

    '扣费明细', '扣下其他','本月工资合计', '社保缴纳金额(公司)', '社保缴纳金额(个人)', '公积金缴纳金额(公司)',

    '公积金缴纳金额(个人)', '本月实缴税额', '实发工资', '个人邮箱']

    for i in df.index.values:  # 获取行号的索引,并对其进行遍历:

    # 枫软  备注1 = 客户编号(长度为15) , 备注2 = 批次,  备注3 = 合同编号(长度最大为5位数), 相关备注 = 包含(产品代码信息,产品代码一般为13位)

    row_data = df.loc[i].reindex(title)

    CustomerNumber = ''

    ContractNumber = ''

    ProductCode = ''

    html = '

    html += '

    '

    for titlestr in title:

    html += '

    %s' %titlestr

    html += '

    '

    html += '

    '

    for titlestr in title:

    html += '

    %s' % row_data['%s' %titlestr]

    html += '

    '

    html += '

    '

    if(row_data['个人邮箱']!='nan' or row_data['个人邮箱']!=''):

    mail(row_data['姓名'],m,html,row_data['个人邮箱'],upass)

    time.sleep(1)

    else:

    err_name += ',' + row_data['姓名']

    print(html,'')

    print('发送失败的人员:'+err_name)

    def rep(strs):  # 替换字符

    strs = str(strs)

    strs = strs.replace(' ', '')

    strs = strs.replace('   ', '')

    strs = strs.replace('\r', '')

    strs = strs.replace('\n', '')

    strs = strs.replace("'", '')

    strs = strs.replace('"', '')

    strs = strs.replace('\\', '\\\\')

    strs = strs.replace('/', '')

    strs = strs.replace('/', '')

    strs = strs.replace('%', '')

    strs = strs.replace(':', '')

    strs = strs.replace(':', '')

    strs = strs.replace('\032', '\\Z')

    strs = strs.replace('?', '')

    strs = strs.replace('☎', '')

    return strs

    def getLocalFile():  # 获取文件

    root = tk.Tk()

    root.withdraw()

    filePath = filedialog.askopenfilename()

    print('文件路径:', filePath)

    return filePath

    input_file = getLocalFile()

    ReadFile(input_file)

    第一步:运行程序选择做好的工资条excel表,然后点击打开

    第二步:输入工资的月份与箱邮密码,这里我输入9月份.

    下面是工资条的excel模板

    展开全文
  • 这次换绑手机深有体会,网易的换绑实在是太难了,我搞了三天,才了解决方法,还有网易人工客服客服。 本文解决的是现在知道账号密码,但是不能换绑安全手机的情况,对于忘记邮箱密码,而且安全手机也注销了,恐怕...
  • 事情发送在两天前,我如标题所示......,它给出的tyningling@163我真的不知道什么时候注册的了,尝试了N个密码登录不上,验证密保吧,看到手机突然想起来,这是拿以前同学的手机注册的.......{这怎么好意思麻烦...
  • 经常有加我,问我怎么破解邮箱密码,开始的时候我还乐意回答,但是多了就会觉得不耐烦了,现在做一个教程让大家知道如何破解邮箱密码.. (现在做这个教程,只是为了让大家了解一下破解邮箱的原理以及操作,希望大家可以...
  • 突然有天你想上传自己的得意之作时,却不知道怎么上传。 当你在网上的教程上传是发现,也就这步一直让你重复登录时,不要着急。 是因为的你github账号没创建过库,没有进行邮件认证,如下图创建新库,然后去你的...
  • 一切都会过去的

    2017-09-05 15:30:19
     放弃吧,真的超级不甘心的,好想找人问问,问谁呢?还是问自己吧!  百度说是缺少驱动,那好吧,下载,可怜我的CSDN积分呀,就是这么被我用没的。还要下载hibernate其他插件,可是非要有redhat账号,用邮箱注册...
  • 可是为什么还有那么多人不会,还要找人帮忙呢? 这个朋友也很有意思,又发了一个BLOG,大意是对刚才的事情表示诧异,然后开玩笑的让求他激活的人,将QQ号码和密码发到一个邮箱。 谁知道还真有人给他发,而且这些密码...
  • 源程序清单中最后一行的括号竟然是“{”,而不是“}”,大哥,你见过哪个程序的结束符是“{”啊,审核员看不懂你的代码但是也不傻啊,知道程序结束符是“}”。 打印申请表的时候记得看每一页什么申请表上的流水有...
  • FuckRouter

    2011-07-20 22:11:17
    如果你看不懂,就帮你买路由器的问下,或者店家问下是什么型号;路由器的说明书上也会有。 在你确认路由器的型号后,而又无法在软件上找到时,可以先试试同一品牌其它型号的选项;路由器型号不正确时,在日志...
  • 也许许多不清楚怎么以一个软件工程的思想去完成软件开发,这就是我发布所有源代码的目的。软件=文档+程序,这是许多知道的;履行这个思想,便是本套程序的开发守则。但由于这个课程设计的时间太短,才2周,...
  • Q: 我改了 Ping 的位置, 怎么知道改成没有? A: 先上网, cfos/cfosspeed 有提供一个命令列参数视窗, cfo pingstat 最后一列的最后一组 IP 就是 Ping 的位置, cfosspeed 用户则打 spd pingstat, 如果你发现没有更改, ...
  • 把它们保存到 KeePass 这个保险柜里,安全系数大大提高,也不用担心因为 Key 多了自己都不知道放在那里了(就像在我乱七八糟的办公桌上昨天还再用的一个文件)。当然了,附件不止是可以保存 Key 文件,有什么绝密...
  • question:孩子离家出走,怎么找回来 answers: ['孩子父母没有结婚,孩子母亲把孩子带走了?这样的话可以起诉要求抚养权的。毕竟母亲也是孩子的合法监护,报警警察一般不受理。'] *********************************...
  • 当然,通常这些地方都会提供贴心的登录信息记忆功能来方便您的使用,可您有没有想过,万一不慎遗失了手机那捡到它的并怀有恶意的也会因此而轻松使用本来只属于您的东西呢?另外,不同的地方对用户名和密码的长度、...
  • vc++ 应用源码包_2

    2012-09-15 14:27:40
    知道不多,但提起国内的暴风影音,我想知道就多了,其实暴风影音就是Media Player Classic,暴风影音只是同我一样从Gabest官方下载到了Media Player Classic的源码,不同的是,暴风影音将Media Player Classic改成...
  • vc++ 应用源码包_1

    2012-09-15 14:22:12
    知道不多,但提起国内的暴风影音,我想知道就多了,其实暴风影音就是Media Player Classic,暴风影音只是同我一样从Gabest官方下载到了Media Player Classic的源码,不同的是,暴风影音将Media Player Classic改成...
  • vc++ 应用源码包_6

    2012-09-15 14:59:46
    知道不多,但提起国内的暴风影音,我想知道就多了,其实暴风影音就是Media Player Classic,暴风影音只是同我一样从Gabest官方下载到了Media Player Classic的源码,不同的是,暴风影音将Media Player Classic改成...
  • vc++ 应用源码包_5

    2012-09-15 14:45:16
    知道不多,但提起国内的暴风影音,我想知道就多了,其实暴风影音就是Media Player Classic,暴风影音只是同我一样从Gabest官方下载到了Media Player Classic的源码,不同的是,暴风影音将Media Player Classic改成...
  • vc++ 应用源码包_4

    2012-09-15 14:38:35
    知道不多,但提起国内的暴风影音,我想知道就多了,其实暴风影音就是Media Player Classic,暴风影音只是同我一样从Gabest官方下载到了Media Player Classic的源码,不同的是,暴风影音将Media Player Classic改成...
  • vc++ 应用源码包_3

    2012-09-15 14:33:15
    知道不多,但提起国内的暴风影音,我想知道就多了,其实暴风影音就是Media Player Classic,暴风影音只是同我一样从Gabest官方下载到了Media Player Classic的源码,不同的是,暴风影音将Media Player Classic改成...
  • JAVA 正则表达式

    热门讨论 2010-01-15 11:16:37
    大家都知道,正则表达式是一种可以用于模式匹配和替换的规范,一个正则表达式就是由普通的字符(例 如字符 a 到 z)以及特殊字符(元字符)组成的文字模式,它用以描述在查找文字主体时待匹配的一个或 多个字符串...
  • vc++ 开发实例源码包

    2014-12-16 11:25:17
    大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了WMI,如果我们能熟练掌握相信你也做的处理.另外WMI除了查询还能修改,比如3389端口,账号,密码,服务启动...
  • 但都是不同各自维护的,之间也没有过交流。就会导致后面新的项目开发相似的业务场景时,面临有多个版本代码的选择。无法做到共同维护一个版本代码,并不断优化和改造,最终实现在这个业务...

空空如也

空空如也

1 2
收藏数 27
精华内容 10
关键字:

知道邮箱号怎么找人