生命不止,折腾不停,最近开始接触了wordpress,发现wordpress真的做得很不错,有很多地方值得学习,很多地方也设置得非常巧妙。在刚安装完了wp后就会有一篇“世界你好”的文章,有点空荡荡,所以我在想可不可用python为wordpress采集一些内容再自动发布上去呢,那么话不多说,让我们开干吧

一、所需库与模块

requests 是一个模拟http请求的库,我们用它来爬取网页。

lxml 是一个解析HTML与XML的库,我们用它来解析网页内容

pymysql 是一个操作MySql的库,我们用它来上传内容到wordpress的数据库

time 是python内置的一个处理时间的模块,我们用它来设置爬取网页的间隔时间

random 是一个python内置的生成随机数的模块,我们用它来生成随机的间隔时间

xpinyin 是一个将汉字转换成拼音的库

二、使用pymysql提交内容到wordpress数据库

一般我们在使用wordpress时都是在后台上面写完然后点击发布,这个过程是这样子的:

浏览器=>form提交=>php=>数据库

而这里我们要跳过前面三个步骤,直接对接数据库:

命令行=>数据库

在通过查看wordpress数据库的wp_posts表的字段后,我写了一段sql语句,并预留了三个位置,为content,title,name,分别代表文章内容,标题,在url显示的名字。

sqlbase="INSERT INTO wp_posts(post_author,post_date,post_date_gmt,post_content,post_title,post_status,comment_status,ping_status,post_name,post_modified,post_modified_gmt,post_parent,menu_order,post_type) VALUE(1,now(),DATE_ADD(now(),INTERVAL '-8' HOUR),'%s','%s','publish','open','open','%s',now(),DATE_ADD(now(),INTERVAL '-8' HOUR),0,0,'post')"

我们要生成一段插入文章的sql语句时就可以这样写

sql=sqlbase % (content,title,name)

接下来要将sql语句提交到wordpress的数据库,这里就需要用到一个python的第三方库pymysql

import pymysql

sqlbase="INSERT INTO wp_posts(post_author,post_date,post_date_gmt,post_content,post_title,post_status,comment_status,ping_status,post_name,post_modified,post_modified_gmt,post_parent,menu_order,post_type) VALUE(1,now(),DATE_ADD(now(),INTERVAL '-8' HOUR),'%s','%s','publish','open','open','%s',now(),DATE_ADD(now(),INTERVAL '-8' HOUR),0,0,'post')"

def upload(title,content,name):
    sql=sqlbase % (content,title,name)
    db=pymysql.connect('数据库ip','数据库用户名','数据库密码','数据库名')
    cursor=db.cursor()
    try:
        cursor.execute(sql)
        db.commit()
    except Exception as e:
        db.rollback()
        print(e)
    db.close()

将提交到wordpress数据库这个操作整合到一个函数中,这样我们在后面使用就会方便一些,将函数中的连接信息修改成wordpress数据库的信息,然后我们来测试一下

>>>upload('我是标题','<p>我是Felix</p>','wo-shi-biao-ti')

因为观察wp_posts表时有些字段内容为空,所以在sql语句中我就没有写,在第一次运行upload()时会有一些提示信息说某些字段为空,忽略即可,然后我们打开wordpress查看

三、爬取内容并上传

在这里,为了方便演示我就选择一个静态的小说网站,在爬取小说网站时,我们主要爬取两种类型的网页,分别为目录页与章节页(文章页)

首先观察章节页,找到章节标题与内容的对应class或者id,通过lxml找到对应的内容,然后根据自己的需求删除或裁剪一些不需要的内容,还有需要注意一下网站的编码问题,这个小说网站直接打印出爬取的内容是乱码所以需要进行一下转码,这里我写了一个的抓取章节页内容的函数

from lxml import etree
import requests

def page(url,booktitle=''):
    response=requests.get(url)
    response.encoding='gbk'
    html=etree.HTML(response.text)
    chapter_title=html.xpath('//h1/text()')[0].split('正文')[1]
    title=booktitle+chapter_title
    content_list=html.xpath('//div[@id="book_text"]/text()')[4:][:-1]
    content=''
    for i in content_list:
        content=content+'<p>'+i+'</p>'

    return [title,content]

测试一下


接下来我们来分析一下目录页,抓取书名与所有章节的链接,可以看到目录页的章节链接是相对地址,所以我们要加上网站的域名将其变为绝对地址,然后我们用time模块与random模块来生成一段时间范围内的随机爬取间隔时间,并且通过第三方库xpinyin来生成wordpress的url所需要的name,下面写了一个menu函数来完成以上操作,输入的参数为目录url与最小爬取间隔时间

import time
import random
from xpinyin import Pinyin
p=Pinyin()

def menu(url,min_time):
    response=requests.get(url)
    response.encoding='gbk'
    html=etree.HTML(response.text)
    booktitle=html.xpath('//h1/text()')[0] #书名
    page_links=html.xpath('//div[@class="article_texttitleb"]//a/@href') #所有章节链接
    i=0
    for link in page_links:
        i=i+1
        try:
            article=page('https://www.tusuu.com'+link,booktitle)
            name=p.get_pinyin(article[0])
            upload(article[0],article[1],name)
            print('page%s finished!' % i)
            time.sleep(random.randint(min_time,min_time+60))
        except Exception as e:
            print('page%s failed!' % i)
            print(e)

    print('All done!')

让我们来测试一下,为了不对别人的网站造成困扰还有不对别人的服务器造成压力,所以这里的最小间隔时间我设置为5分钟即300秒,程序就会在5到6分钟之间进行一次爬取。

>>>menu('https://www.tusuu.com/book/txt21094.html',300)

然后我们隔一段时间再来看一下,因为仅用于测试,所以在第九章时便Ctrl+C退出

四、为文章添加分类(可选)

wordpress并没有强制要求文章要设置分类,如果你需要为文章添加分类,可以看一下下面的方法。

wordpress数据库跟分类有关的有三个表wp_terms,wp_term_relationships,wp_term_taxonomy

wp_term是记录所有分类

wp_term_relationships是记录文章与分类关系

wp_term_taxonomy是记录分类的父分类,分类描述与分类下的文章总数

我们首先在wp_term里找到分类的id,然后在插入文章后与commit之前通过cursor.lastrowid获取最新文章的id,这时候添加分类,接着为wp_term_taxonomy表中分类的文章统计数加一,最后再commit,这里改写了一下upload函数

category_sqlbase="INSERT INTO wp_term_relationships(object_id,term_taxonomy_id,term_order) VALUE(%s,%s,0)"
count_sqlbase="UPDATE wp_term_taxonomy SET count=count+1 WHERE term_id ='%s' "

def upload(title,content,name,cate_id):
    sql=sqlbase % (content,title,name)
    db=pymysql.connect('数据库ip','数据库用户名','数据库密码','数据库名')
    cursor=db.cursor()
    try:
        cursor.execute(sql)
        sql2=category_sqlbase % (cursor.lastrowid,cate_id)
        sql3=count_sqlbase % cate_id
        cursor.execute(sql2)
        cursor.execute(sql3)
        db.commit()
    except Exception as e:
        db.rollback()
        print(e)
    db.close()

upload函数多添加了一个参数cate_id为分类的id,我们来测试一下,我为wordpress添加了一个新闻分类,通过查看wp_terms表得知新闻分类id为4

>>>upload('我是新闻标题','我是新闻内容','wo-shi-xin-wen-biao-ti',4)

现在就可以看到文章被添加到了新闻的分类了,当然你也可以动态地添加新的分类,然后通过sql语句用名字找到对应的id。

五、其他

不同的网站一般结构都是不同的,所以上面爬取部分的代码在别的网站上一般也是用不了的,甚至因为网站更新在同一个网站也可能用不了,所以爬取部分大家可以根据自己的需要修改一下。

对于动态页面我们可以调用页面上的一些js,获取返回的结果。

如果你担心被网站禁ip,可以挂代理,或者将采集程序放到挂机宝上,也可以选择模拟百度UA,这个可以在requests上设置。

以上所使用网站仅用于学习探讨,请勿对他人网站进行频繁访问攻击,并注意版权问题,造成法律纠纷与财产损失与本站无关,请自行负责!

评论

Felix 管理员

今天更新了一下文章,添加了设置文章分类的方法!

回复

Felix 管理员

wordpress是挺不错的,生态很好

回复

  • 最新随笔

  • 对我来说,写东西很好的一点是,自己写的东西,后面基本都会再看,一遍或者很多遍。这样就可以很好地发现自己之前的漏洞,并看看现在有什么变化。
  • 前段时间应该是百度更新了页面结构,那个黑帽泛目录网站我也没有去更新代码,就导致了文章页没有相关推荐,没想到这时候谷歌开始收录了,而且效果还不错。其实之前在谷歌一直不收录的时候就有怀疑过没有尽头的相关推荐很可能是导致不收录的原因,不过也一直没有去管它,而这次误打误撞的收录也恰恰证实了这个想法。(btw,其实这个网站从出现固定链接开始叫泛目录应该不太准确)
  • 最近吉他谱网站iloveguitar上线了,这几天在填充一些内容花了一些时间,这个网站也是使用flask作为后端框架的,前端UI框架使用的是bootstrap4,喜欢吉他的小伙伴欢迎来这里逛一逛哟,有什么建议欢迎在留言板提一下哟。
  • 那个泛目录网站发布了几个星期了,百度没怎么来爬,收了一个首页,谷歌直接就不收了,倒是一个查外链的网站天天来爬(笑哭),还有建议一点就是泛目录还是用在有建站历史的域名比较好(因为已经过了审核期),新域名就尽量不要用了。
  • 刚刚又发了一篇,最近文章百度都不太收录,orz,哈哈
  • 之前那个用python做的静态资源服务器,我又发现了一大用处,现在我都用来在局域网里的不同电脑之间传输文件,真的好用哈哈
  • 今天交换到了博客有史以来第一条友情链接
  • 刚刚更新了一下博客,修复了一些bug,添加了一个项目页