一、所需库与模块
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查看

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

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)

四、为文章添加分类(可选)
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。

为程序加一个添加特色图片的功能,即文章的封面,观察了一下wordpress的数据库发现,特色图片需要先添加到wordpress的媒体库中,然后才能设为文章的封面,所以下面就跟大家分享一下这之间的一些记录。

一张图片要上传到wordpress的媒体库,需要在数据库添加两条记录。

一是在wp_post表中添加一条记录(图片和文章都用同一个表),这里面最主要的就是图片的url,然后另一条记录就是添加到wp_postmeta,记录图片的一些信息,我们在提到的_wp_attachment_metadata字段就是记录图片的信息。
wp_postmeta表中的post_id对应wp_post表中的ID。

一开始在看到wp_postmeta中_wp_attachment_metadata对应的键值我是有点懵逼的,这里面有些直接看过去还是能明白是什么意思,比如宽、高、文件名,以及对应的值,不过那个s后面的那个数字不知道是什么意思,在百度搜索无果后,便到谷歌搜索了一下,才搞懂了s的意思;我先把_wp_attachment_metadata的值格式化给大家看一下。
我举个例子s:5:"width";,在这里面的width是五个字符,所以s后面就是5,大家可以看一下上面是不是这么回事。

明白是什么意思后,将图片上传到媒体库就非常简单啦;接下来我们再解决另一个问题,如何将图片设为文章封面即wordpress所说的特色图片呢,这个比较简单,在wp_postmeta添加一条记录就行,像下面这样。
post_id对应文章的ID,meta_value对应图片的ID,都是对应wp_post表的ID。

添加一个参数,为图片的url,图片添加到媒体库并设为文章封面图的小功能,若无图片则不添加封面。
通过requests和BytesIO获得图片,再通过PIL获得图片的长宽,用于_wp_attachment_metadata的值

import pymysql
import requests
from io import BytesIO
from PIL import Image

post_sqlbase=r"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')"
category_sqlbase=r"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' "
image_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,guid,menu_order,post_type,post_mime_type) VALUE(1,now(),DATE_ADD(now(),INTERVAL '-8' HOUR),' ','%s','inherit','open','closed','%s',now(),DATE_ADD(now(),INTERVAL '-8' HOUR),0,'%s',0,'attachment','image/jpeg')"
postmeta_sqlbase="INSERT INTO wp_postmeta(post_id,meta_key,meta_value) VALUE ('%s','%s','%s')"
profile_base='a:4:{s:5:"width";i:%s;s:6:"height";i:%s;s:4:"file";s:%s:"%s";s:5:"sizes";a:1:{s:4:"full";a:3:{s:5:"width";i:%s;s:6:"height";i:%s;s:4:"file";s:%s:"%s";}}}'

def getSize(img_url):
    response=requests.get(img_url)
    f=BytesIO(response.content)
    image=Image.open(f)
    return image.size

def upload(title,content,name,cate_id,img_url):
    sql=post_sqlbase % (content,title,name)
    db=pymysql.connect('数据库ip','数据库用户名','数据库用户密码','数据库名')
    cursor=db.cursor()
    try:
        cursor.execute(sql)
        post_id=cursor.lastrowid
        sql2=category_sqlbase % (post_id,cate_id)
        sql3=count_sqlbase % cate_id
        cursor.execute(sql2)
        cursor.execute(sql3)

        if img_url:
            file_name=img_url.split('/')[-1]
            image_name=file_name.split('.')[0]
            thumbsql=image_sqlbase % (image_name,image_name,img_url)
            cursor.execute(thumbsql)
            thumb_id=cursor.lastrowid
            width,height=getSize(img_url)
            metasql=postmeta_sqlbase % (post_id,'_thumbnail_id',thumb_id)
            profilesql=postmeta_sqlbase % (thumb_id,'_wp_attachment_metadata',profile_base % (width,height,len(file_name),file_name,width,height,len(file_name),file_name))
            cursor.execute(metasql)
            cursor.execute(profilesql)

        db.commit()
    except Exception as e:
        db.rollback()
        print(e)
    db.close()

在爬取内容时,获取内容出现的第一张图片的url传入upload函数中,作为文章的封面图,若无则不传参
因为图片想要设为特色图片是需要先添加在媒体库的,所以在媒体库会储存这张封面图
以上就是关于如何为wordpress上传图片与设为特色图片的一些小方法啦,因为图片都是外链图片,所以爬取的图片都不是保存在本地,只是保存图片的URL而已

最后修改:2022 年 05 月 04 日
如果觉得我的文章对你有用,请随意赞赏