同福

Python做个搜索引擎(5)网页蜘蛛代码编写(一)【20201010】

介绍

介绍

昨天我们明白了网页蜘蛛的工作原理,今天就可以开始根据这个原理进行代码的编写了。

虽然python也可以编写面向过程的程序,但是使用面向对象的方式编写程序是一个好的习惯,它更加可以满足各种复杂的业务逻辑的需要,同时代码也更容易读懂。

福哥要先开发网页蜘蛛程序,所以先建立一个Spider对象,用它来实现网页蜘蛛的全部功能。

Spider对象

首先,福哥先祭出Spider对象,这个对象就是网页蜘蛛这个对象的抽象类了。这个对象要实现网页蜘蛛的全部功能(当然仅仅是我们需要的功能),里面包含了建立爬取任务队列、爬取网页内容、分析网页内容、保存增量网页等等功能。

代码

这是Spider对象的代码(未完成),保存在 lib/Spider.py 里面,作为一个自定义的软件包使用。

import pymysql
from selenium import webdriver
import time
import re


class Spider:
    mysqlCN = None
    mysqlCS = None
    chrome = None
    chromeOpts = webdriver.ChromeOptions()
    # chromeOpts.add_argument("--headless")
    chromeOpts.add_argument("--disable-gpu")
    chromeOpts.add_argument("--no-sandbox")
    chrome = webdriver.Chrome(options=chromeOpts)

    # open mysql
    def open(self):
        try:
            self.mysqlCN = pymysql.connect("192.168.2.168", "tfse", "abcdef")
            self.mysqlCS = self.mysqlCN.cursor()
            self.mysqlCN.select_db("tfse")
        except Exception as e:
            print(e)
            exit()

    # get pending domains
    def getPendingDomains(self, nums):
        try:
            self.mysqlCS.execute("SELECT * FROM websites ORDER BY nextFetchDT ASC, lastFetchDT DESC LIMIT " + str(nums))
            rows = self.mysqlCS.fetchall()

            return rows

        except Exception as e:
            print(e)
            self.mysqlCN.rollback()

        return None

    # fetch a domain URL
    def fetchDomainURL(self, domainName):
        # open page of domain
        url = "http://www." + domainName
        print("打开网址:" + url)
        self.chrome.get(url)

        # find all a tags
        links = self.chrome.find_elements_by_tag_name("a")
        for link in links:
            myHref = link.get_attribute("href")
            if self.isWebPage(myHref, domainName):
                self.saveWebPage(myHref)
            else:
                self.saveDomainPage(myHref)

    def isWebPage(self, url, domainName):

        return True

    def saveWebPage(self, url):
        print("WebPage: " + url)

    def saveDomainPage(self, url):
        print("DomainPage: " + url)

    # close
    def close(self):
        try:
            self.mysqlCS.close()
            self.mysqlCN.close()
        except Exception as e:
            print(e)
            exit()

主程序

这里给出蜘蛛的主程序,主程序通过上面的Spider对象实现爬取网页的功能,保存在 Spider.py 里面。

from lib.Spider import *

mySpider = Spider()
mySpider.open()
domains = mySpider.getPendingDomains(10)
for domain in domains:
    mySpider.fetchDomainURL(domain[1])

mySpider.close()

讲解

下面福哥来讲解一下这个代码的逻辑,大家可以跟着福哥的思路一起读这段代码,了解其中的设计和功能。

主程序

主程序首先是通过from和import将Spider软件包里的对象导入进来了。

接着初始化Spider对象到mySpider变量里面。

通过Spider对象的open方法建立MySQL数据库的连接以及初始化工作。

使用Spider对象的getPendingDomains方法从MySQL数据库里查询出10个待处理的域名。

通过for循环对10个域名逐个进行单独的处理,使用Spider对象的fetchDomainURL方法对单独的一个域名进行爬取操作

最后使用Spider对象的close方法关闭MySQL数据库的连接。

Spider对象

接着福哥来对Spider对象的每个方法进行一个简单的介绍,大家可以了解一下。

open

这个方法里面对属性mysqlCN和mysqlCS进行了初始化,主要就是要建立MySQL数据库的连接和选择tfse数据库。

一般情况下,需要初始化的操作都会单独放到一个对象的方法里面进行处理。

getPendingDomains

这个方法里面通过查询tfse库的websites数据表得到了最前面的若干条网站域名信息。这里面有个小问题,就是如果我们后面不去更新nextFetchDT字段和lastFetchDT字段的值,则这个方法会始终返回相同的若干条网站域名信息了。所以,在后面我们一定要合理的设计如何去更新nextFetchDT和lastFetchDT字段的值来保证爬取的过程是雨露均沾的。

fetchDomainURL

这个方法是蜘蛛程序的关键了,在这个方法里面我们会尝试去访问一个网址(URL),然后分析这个网址的网页信息,并且会去提取更多的子级网址以备今后进行采集分析处理。

这个方法目前只是实现了采集网页的链接这个基础功能,后面我们会逐步对它进行完善。

isWebPage

这个方法福哥没有实现它,它的作用是判断采集到的链接是内部链接还是外部链接,如果是外部链接就意味着这会是另外的一个网站的网页了,我们就需要区别处理了。

saveWebPage

这个方法福哥也没有实现它,它的作用很容易理解,就是要把网页网址保存到webpages数据表里面。

saveDomainPage

这个方法福哥也没有实现它,它的作用也很容易理解,就是要把网址域名保存到websites数据表里面。

close

这个方法需要在全部逻辑执行完毕之后运行,它的作用就是关闭MySQL数据连接,释放资源。

输出

执行主程序会打印爬取网站后得到的域名信息

1ec1174a937de52e.jpg

总结

大家可以看出来福哥给出的这个Spider对象还很不完善,很多方法根本没有去实现它的逻辑。在这里福哥想告诉大家,编写代码就是这样一步一步的推进的,不会有那个程序员可以在一开始就设计好全部需要的对象和函数,进而一一实现它们,最后运行时候还没有任何问题一次通过的,都是需要在逐步推进的过程中一步一步的磨合才能完成的。

另外,福哥会保留一部分关键函数的实现的代码,这些就留给童鞋们自己动脑筋去实现了。还是那句话,完全的代码贴出来,会造成有些童鞋的惰性,直接复制粘贴过来,没有任何营养,这样是无法学好编程的。

P.S.

微信公众号的文章发出去之后是不能编辑的,但是福哥偶尔会修复一些描述不到位、示例不正确、结构不清晰等等的文章错误,这些只能在网站上才能看到最新版本内容,望大家知晓~~