2018年6月

Google AdSense 下号了

历时一个月,七次申请,终于批号了,哈哈,开心,其实也没想到这个站竟然能通过审核,因为之前一直都是被拒的,所以昨天又另外搞了一个站打算如果再被拒,就换站试试了,结果竟然下号了,哈哈,开心,大概记录一下这一个月的操作,不过这些操作不一定是通过审核的因素,感觉跟审核的人可能也有些关系。

第一次申请,网站内容很少,大概十几条文章还是去年发的,然后就一直没管过,google有12条的收录,流量零,结果毫不意外,被拒,被拒之后,因为登陆adsense后,提示说是网站上发现一些违规问题,还一度以为是网站内容不符合google政策,所以被拒了。

第二次申请,怀疑网站内容不行,所以直接就换了另外一个旅游类型的网站,也是只有几十条文章,也是去年发的,google的收录自然也不多,流量也是零,结果也是毫不意外的被拒,不过这次被拒确定不是因为网站内容不符合google政策,应该是网站内容太少所以被拒的。

第三次申请,仍然使用第一次申请用的网站,增加了几十条文章,整站大概有50多条的文章,文章都是直接复制粘贴的,没有做伪原创,只是把发布时间均匀分布了一下,假装是去年到现在不间断更新的,然并卵,还是被拒了,被拒的邮件还是一样的

Site does not comply with Google policies: We're unable to approve your AdSense application at this time because your site does not comply with the Google AdSense program policies or adhere to the Webmaster Quality guidelines. It's our goal to provide our advertisers sites that offer rich and meaningful content, receive organic traffic, and allow us to serve well-targeted ads to users. We believe that currently your site does not fulfill this criteria.

Here are some recommendations to help you improve the user experience on your site and comply with AdSense criteria:

  • It’s important for sites displaying Google ads to offer significant value to the user. As a publisher, you must provide unique and relevant content that gives users a reason to visit your site first.
  • Don’t place ads on auto-generated pages or pages with little to no original content.
  • Your site should also provide a good user experience through clear navigation and organization. Users should be able to easily click through your pages and find the information they’re seeking.

第四次申请,因为之前加了几十篇文章仍然被拒,所以没有被拒后马上申请,打算每隔两天更新一下,等了几天,这几天也更新了两条文章,然后google的收录增加了一些,结果还是一样的,被拒,不过这次被拒发的邮件跟之前三次不一样,增加了一些内容

Insufficient content: To be approved for AdSense and show relevant ads on your site, your pages need to have enough text on them for our specialists to review and for our crawler to be able to determine what your pages are about.

To resolve this issue, please work through the following suggestions:

  • Make sure that your pages have sufficient text - websites that contain mostly images, videos or Flash animations will not be approved.
  • Your content should contain complete sentences and paragraphs, not only headlines.
  • Ensure that your website is fully built and launched before you apply for AdSense - do not apply while your site’s still in a beta or “under construction” phase or only consists of a website template.
  • Place the ad code on a live page of your website. It does not have to be the main page, but test pages that are empty except for the AdSense ad code will not be approved.
  • Provide a clear navigation system for your visitors so that they can easily find all of the sections and pages of your website.
  • If you’d like to monetize YouTube videos, please apply for the YouTube monetization program. Note that blogs and websites that contain only videos will not be approved.

直接说内容不足了,既然这样,那就再加些内容好了,本来想着直接加到100条文章的,结果比较懒,复制粘贴到85条就懒得再加了,也是等了几天,google site到60多条记录,实际放出来的收录有20多条,然后这几天也开始每天刷点流量,另外也给网站加上了https,结果被拒了,被拒的原因有点尴尬

Unable to review your site: While reviewing http://domain, we found that your site was down or unavailable. If you applied through an AdSense host partner, such as YouTube or Blogger, your hosted site was not available or not found (removed) at the time of the review.

无法查看网站,什么鬼,已经对http做301跳转到https了,怎么会无法打开网站,这还真是奇怪了。

第六次申请,因为上次被拒原因有点尬,所以改成直接用https的网址申请,google site查到100多条收录,放出来30多条,继续每天刷着流量,然结果仍是被拒,被拒邮件又跟一开始一样样了。

第七次申请,感觉估计还是站不行,所以一直被拒,打算再被拒的话,就准备换站试试了,这次也只是增加了7条文章,还是没能加到100条,哈哈,只有92条文章,google的收录倒是增加了不少,不知道是不是因为这几天一直有刷着流量的原因,site查到200多条记录,放出来的不多还是只有30多条。

本来想着应该还是会被拒,等了三天,结果,哈哈,今天打开邮箱一看,竟然是获批的邮件,意外不意外,惊喜不惊喜,从5月28号到6月27号,历时整整一个月,Google AdSense终于下号了,嗯,用的是英文站,申请的是美号。

总结,其实也没啥可总结,因为套路并没有摸清楚,对google adsense的审核标准也还是不了解,不过有个百十来篇文章,适当刷点流量,每天有百十来个IP应该就可以了,最好是文章的内容可以适当的搞一搞,如果纯复制粘贴,可能不太容易过,也可能是复制粘贴选的源站不咋的,像我被拒这么多次一样,搞不好也是运气,碰巧最后这次审核的人标准比较宽松,哈哈,所以不好说呢,毕竟是人工审核。

其实说起来这都不算是第一个google adsense账号了,在很久很久以前,大概2010年就下过一个号了,是不是很久了,呵呵,国号,也是申请了好多次过的,不过那会下号之后,emmm~就只是下了一个号,然后就一直都木有搞,说来还真是惭愧,至今那个号也才十多刀吧,算了,不多说了,说多了都是泪,搞站搞站,搞流量搞流量,没有流量,神马都是浮云,搞起来~

把一个wordpress网站源码重新上传,用了一个闲置域名,准备搭起来用来申请adsense,之前用了个随便复制粘贴了些文章的站申请了好几次,也刷了些流量,结果还是一直都过不了,想到还有这个站,整体看起来大概应该还可以,比那个复制粘贴的站好一些,所以准备上线刷上几天流量,再申请adsense试试。

这年月搭个站自然是要带上https的,所以就相当于是给wordpress的网站换域名并启用ssl了,直接进到到数据库改,先把wp_posts表里的旧域名都换成新域名

UPDATE wp_posts SET post_content = REPLACE(post_content, 'http://old.com', 'https://new.com');
UPDATE wp_posts SET guid = REPLACE(guid, 'http://old.com', 'https://new.com');

再把wp_options表里的siteurl和home两个配置项换成新域名,其他的一些插件和主题配置项中也可能有域名,可以手工逐条查找修改,也可以直接sql一下修改,不过因为有些配置项是serialize序列化的,所以还是在phpmyadmin里手工修改好一些

UPDATE wp_options SET option_value = replace(option_value, 'http://old.com','https://new.com');

另外wp_comments,wp_postmeta表里可能也有旧域名,不过这些就按需修改了。

修改完成,打开新换的域名看看,结果,“此页面不能正确地重定向”,什么鬼,有缓存?还有没改到地方?不应该呀,明明能改的都改了,怎么会这样?

Cloudflare Flexible SSL导致WordPress开启https后无限循环重定向

又是一通查看,数据库,网站文件各种检查,结果还是一直无限循环重定向,前后台都是,都打不开,尴尬了,中间还一度以为是浏览器301缓存的原因,因为一开始没修改完,访问了一下新域名直接给301跳转到旧域名上,然后发现这个301缓存是永久的,不清除缓存的话,就算是取消301跳转,浏览器这边还是会直接跳转,当然也可以直接在F12的网络选项卡里选择禁用缓存。

各种尝试查找,都无果,想着这个重定向是在wordpress内部执行的,把重定向取消掉试试,在主题模板的functions.php文件里加上一行代码

remove_filter('template_redirect', 'redirect_canonical');

加上之后,确实可以访问了,不过js,css什么都没有加载,因为这些资源文件都还是http加载的,被浏览器默认禁用了,所以整个页面没有样式,难道是后台还有什么插件或者主题设置的不对,登陆后台试试,后台也是无限循环重定向,所以要把后台的重定向也取消掉,在wp-includes/functions.php文件里找到force_ssl_admin函数,直接在这个函数第一行加上return false

function force_ssl_admin( $force = null ) {
        return false;  // 加到这里      
        static $forced = false;

        if ( !is_null( $force ) ) {
                $old_forced = $forced;
                $forced = $force;
                return $old_forced;
        }

        return $forced;
}

然后后台也确实可以访问了,不过跟前台一样,也是没有样式,css跟js什么也都是http加载的,所以被浏览器默认禁用了,不过这时惊奇的发现,不带https,直接使用http访问后台时,竟然没问题,嗯,前台直接用http访问也是没有问题,这还真是好奇怪,emmm,难道是这套源码里还有什么不为人知的改动,正好wordpress版本也不是最新的,干脆直接升级到最新版本试试,然并卵,升级之后还是一样的问题,真是尴尬了,难道是缓存的问题?不过缓存是没有的,因为没有加缓存插件,配置什么的也应该都没有问题,这下应该不是wordpress哪里不对的问题了,那问题出在哪呢?奇怪emmm~

想来想去,试来试去,还是无果,这真是尴尬了,而且不应该呀,应该不是wordpress的问题,那是哪里的问题呢?会不会是cloudflare的问题?没错这个域名是放到cloudflare上的,我去,该不会真是cloudflare的问题吧,赶紧去看看,把cloudflare的缓存清掉,然无用,继续看,这个SSL: Flexible是个什么鬼,看看帮助

Flexible SSL: You cannot configure HTTPS support on your origin, even with a certificate that is not valid for your site. Visitors will be able to access your site over HTTPS, but connections to your origin will be made over HTTP.

所以这个Flexible SSL就是说,不需要源网站配置SSL证书,用户访问网站的时候使用https请求,而Cloudflare会直接向源站服务器发送http请求,也就是说,用户->https->cloudflare->http->源站,用户到Cloudflare这段是加密的https访问,Cloudflare到源站服务器这段是不加密的http请求,用这样的方式开启网站的https。

我勒个去,搞了半天,竟然是这样的,虽然是直接是使用https访问的网站,而且在wordpress里也都改成了https,但是实际上Cloudflare发到源站的请求是http的,而wordpress检测到是http的请求又会直接发送重定向到https的响应,然后再一次 https->http->https,然后...就陷入了一个无限循环重定向的死循环中,好吧,折腾这么久,结果是这么个原因,所以解决方法也就很简单了,直接把Cloudflare的SSL选项改成 Full 或者 Full(Strict),让Cloudflare默认访问https

Cloudflare Flexible SSL导致WordPress开启https后无限循环重定向

OK,问题解决,网站可以正常使用https访问了。

搞一个aws账号,创建了一个ec2,用的ubuntu镜像,不过发现ec2不能直接用账号密码登录,需要用证书登录,好吧,那就用证书登录吧

putty

putty->Connection->Auth->Private key file for authentication 里选择在aws下载的私钥证书 xxx.pem, 然后登陆,结果提示如下错误:

Unable to use key file "xxx.pem" (OpenSSH SSH-2 private key)

What? 不能使用OpenSSH SSH-2 private key,密钥有问题?什么情况?好吧,没用过证书登录,以前都是直接用账号密码登录的,这有点尴尬,搜了一下,貌似是OpenSSH私钥与putty使用的私钥格式有点不一样,需要转换一下,好吧,去 https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html 这里下载一个puttygen.exe

puttygen

puttygen->Conversions->Import Key->Save private key 里把在aws下载的私钥证书 xxx.pem 转换为 xxx.ppk,再用putty尝试登陆,结果提示如下:

Please login as the user "ubuntu" rather than the user "root".

好吧,这镜像还是默认禁用root的,那就用ubuntu登陆吧,OK,登陆成功,这,虽说用证书是免密登陆,不过这一顿操作,反正也是测试服务器,还是改成账号密码登录吧

编辑 /etc/ssh/sshd_config
sudo vim /etc/ssh/sshd_config

修改 PermitRootLogin prohibit-passwordPermitRootLogin yes
这是设置允许root登陆

修改 PasswordAuthentication noPasswordAuthentication yes
这是设置允许使用账号密码登录

保存退出并重启ssh服务
sudo service ssh restart

哦对,还差一步,要重置root的密码

sudo passwd root

OK,现在就可以直接使用账号密码登录,也可以使用root账号直接登陆了。

最近搞了个简单的文章转视频上传到youtube,需要采集点文章来生成视频,所以打算用Scrapy采集点今日头条的文章,目标是抓取其中一个分类的几十篇文章内容和下载其中的图片导出json数据文件用来生成视频,简单做一下记录。

打开头条网站,有多个分类,打开娱乐版块,查看源代码发现是没有文章内容,看来是js加载的,F12查看分析找到文章数据的接口链接
https://www.toutiao.com/api/pc/feed/?category=news_entertainment&utm_source=toutiao&widen=1&max_behot_time=0&max_behot_time_tmp=0&tadrequire=true&as=A1B5FBF1A7CA537&cp=5B172A5593E7AE1&_signature=oNvqZwAA-9u3ZjcQBbvTJ6Db6n
在浏览器里直接打开这个链接,很好,返回json结构的数据,刷新一下,返回不同的文章,嗯,这么easy,一个链接就搞定了。

那还等什么,新建Scrapy项目抓一下试试,哈哈,什么也没有,看一下Scrapy的输出 DEBUG: Forbidden by robots.txt 原来是头条禁止爬虫抓取了,需要设置Scrapy的参数 ROBOTSTXT_OBEY = False,再试一下还是不行,直接返回

{
  "message": "error",
  "data": [],
  "has_more": false
}

没有文章数据,构造header加上User-Agent跟Referer

headers = {
        "Referer": "https://www.toutiao.com/ch/news_entertainment/",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0"
    }

OK,正常返回数据了,多次请求,嘿,返回的文章基本都一样,看来还是不可以哦,再加上cookie试试,用固定的 tt_webid=6563786091257824776;,多次请求,发现跟浏览器里刷新是一样的了,会返回不同的文章,倒是基本可以满足一次抓取几十篇文章需求了。

不过既然搞了那再深入研究一下看看这几个参数是干啥的嘛

category=news_entertainment 分类,对应头条的分类,news_entertainment是娱乐

utm_source=toutiao 固定

widen=1 固定

max_behot_time=0max_behot_time_tmp=0值相同,是个时间戳,初次请求是0,每次请求返回的数据中都会带有下一次请求的时间戳 "next": {"max_behot_time": 1528358256}

tadrequire=true带广告,false不带广告

as=A1B5FBF1A7CA537cp=5B172A5593E7AE1是简单的时间加密,网上搜一下,很容易找到python的翻译代码,不加这两个参数,也能返回文章数据

def get_as_cp():
    t = int(math.floor(time.time()))
    e = hex(t).upper()[2:]
    m = hashlib.md5()
    m.update(str(t).encode(encoding='utf-8'))
    i = m.hexdigest().upper()
    if len(e) != 8:
        as_key = "479BB4B7254C150"
        cp_key = "7E0AC8874BB0985"
        return as_key, cp_key
    n = i[:5]
    a = i[-5:]
    r = ""
    s = ""
    for k in range(5):
        s += n[k]+e[k]
        r += e[k+3]+a[k]

    as_key = "A1" + s + e[-3:]
    cp_key = e[0:3] + r + "E1"

    return as_key, cp_key

_signature=oNvqZwAA-9u3ZjcQBbvTJ6Db6n 是个签名,测试发现这个参数值的生成跟浏览器UA有关,换个UA就没有数据返回了,_signature跟UA匹配才会返回数据,网上倒是没有找到python的翻译代码,看来这个有点难搞,先用固定值,构造链接

url = 'https://www.toutiao.com/api/pc/feed/?category=news_entertainment&utm_source=toutiao&widen=1' \
          '&max_behot_time={0}' \
          '&max_behot_time_tmp={0}' \
          '&tadrequire=true' \
          '&as={1}' \
          '&cp={2}' \
          '&_signature=oNvqZwAA-9u3ZjcQBbvTJ6Db6n'

发现第一次请求max_behot_time=0时正常返回文章数据,之后带上新的时间戳后,就没有数据了,看来这个签名还跟时间戳有点关系,找一下js代码看看,

function e(t) {
        var e = ascp.getHoney(),
            i = "";
        window.TAC && (i = TAC.sign("refresh" === t ? 1 : r.params.max_behot_time_tmp)), r.params = _.extend({}, r.params, {
            as: e.as,
            cp: e.cp,
            max_behot_time: "refresh" === t ? 0 : r.params.max_behot_time_tmp,
            _signature: i
        })
    }

果然,签名的生成跟max_behot_time_tmp的值有关,貌似是用TAC.sign()函数生成,找一下这个函数看看,emmm~,乱码~,尴尬了,在F12控制台敲入TAC.sign,发现还是乱码是什么鬼,这个函数还搞了一下加密?算了,算了,大道至简,还是搜搜看有没有人搞过吧,不过没有找到python的翻译代码,让我自己写,哈哈,那还是写不出来的,不费这个劲了,直接用解析js的方式来获取这个值吧,搜了一下,可以用pyExecJS这个模块来解析js,但是直接把头条的js文件加载进来发现会报错,不过倒是找到一份整理过的js代码 toutiao-TAC.sign.txt,https://bbs.125.la/thread-14115046-1-1.html,感谢nliger2015的分享,可以直接拿来用没有报错。

def get_as_cp_signature(max_behot_time=0):
    with open('toutiao-TAC.sign.txt', encoding='utf-8') as f:
        js = f.read()
    ctx = execjs.compile(js)
    s = ctx.call('get_as_cp_signature', max_behot_time)
    return json.loads(s)

哈哈,很好,可以直接返回as,cp,_signature三个参数值,不错不错,之前as和cp的翻译函数不需要了,获取文章列表数据基本差不多了,接下来开始搞文章详情。

先是直接用xpath获取内容,哈哈,结果取回来的全是None,嗯?xpath写的不对?放到浏览器测试没问题的,查看源代码看一下,我去,详情页的内容也是用js加载的,想要的内容都在js里呢,都放到BASE_DATA这个变量里了,是json结构的对象,不过不能直接用json模块解析,因为不全是用的双引号,json模块解析会报错,唉,好吧,既然在js里,那直接用js解析一下,要是能直接返回BASE_DATA这个变量对象,不就是拿到了文章的结构数据,用起来不就更省事了,不就是想怎么用就怎么用了,哈哈。

data = response.xpath('//script[contains(text(),"var BASE_DATA")]/text()').extract_first()
    if data:
        data = data + "\nvar a = function(){return BASE_DATA;}"
        ctx = execjs.compile(data)
        data = ctx.call('a')
        print(data['articleInfo']['title'])

先用xpath取出需要的js代码部分,再加个函数return这个变量,然后用pyExecJS解释执行一下,返回文章详情数据,执行下来,倒是没问题,也正常返回数据了,而且确实是直接可以拿来用的,不过输出一下,中文全变成了“锟斤拷锟斤拷锟叫★拷锟斤拷锟斤拷钱锟斤...”,这是什么鬼?编码有问题?尝试转码后再传递给js执行,结果报错,emmm,什么鬼,各种编码解码尝试无果,放弃js解析,改用正则,哈哈,还是正则好用,指哪拿哪

data = response.xpath('//script[contains(text(),"var BASE_DATA")]/text()').extract_first()
data = re.search(r"title:\s*'(.*?)',\s*content:\s*'(.*?)',[\s\S]*?tags:\s*(\[.*\]),", data)
if data:
    title, content, tags = data.group(1, 2, 3)

直接拿到title,content,tags的数据,再对拿到的数据进行一下处理,加个下载图片的pipeline把图片也下载下来,嗯,基本搞定,采集导出的json数据文件可以直接用来生成视频。

还有点小问题,重复文章数据,因为写这个小爬虫,就是采集文章用来生成视频的,不过生成视频比较慢,嗯是挺慢的,所以一次采集个几十篇文章即可,在一次抓取中,返回的文章数据是没有重复的,不过下一次再重新启动爬虫抓取时,就会跟上次采集的文章有重复的,时间间隔不长的话,重复的还不少,也加上cookie固定ttweb_id的值试了下发现效果不大,一样会有重复的数据,所以就把每次抓取的文章链接存到文件里,每次开始抓取前,先读入存放链接的文件把已经抓取的链接放到列表里,每次发送文章详情页request前先判断一下是不是抓取过,在列表中的链接直接跳过,等到抓取完成爬虫关闭时,再把新抓取的链接追加到文件里,等抓取的链接比较多的时候,这种方式可能不太好,不过没关系,满足当前的需求就可以了,哈哈。