Mathpix收费了?快使用API吧,个月免费识别1000次!

最近,数学公式OCR神器Mathpix 开始收费,一个月只能免费用50次,再用就得5刀一月,小伙伴们顿时叫苦不迭。想想这种套路国内互联网公司不知道玩了多少次了,从滴滴打车到美团外卖再到共享单车,都是先培养消费习惯再收割一波韭菜。但是当初大家都只顾享受免费识别公式带来的快感,哪知由俭入奢易,由奢入俭难,现在再手敲公式真是痛苦不堪。

目前来看,由于需要联网识别,基本没有空子可钻。当然也不排除个别神人用大量邮箱来白嫖:如何看待 Mathpix 收费?​www.zhihu.com

但是,Mathpix官网对于一些有能力的开发者还是开了一扇后门的:Mathpix OCR​mathpix.com

从它提供的OCR API来看,一个月可以免费使用1000次!在之后一直到10万次都是0.004美元(约合0.028元)一次。对个人使用者而言,1000次已经基本够了。

首先需要登录平台:MathpixOCR login​dashboard.mathpix.com

登录之后需要输入信用卡信息,完成账号激活。

激活后,将会显示API的id和key:

API官方文档:Mathpix API v3 Reference​docs.mathpix.com

Github上给出了具体示例:Mathpix/api-examples​github.com

以Python为例,只需要在mathpix.py中填写自己的id和key,再调用simple.py就能运行。从示例来看,它识别的是本地图片。我在它的基础上加入了识别剪贴板的代码,从而达成与Mathpix相似度为99%的使用体验。

import os
import base64
import requests
import json
from PIL import ImageGrab
#
# Common module for calling Mathpix OCR service from Python.
#
# N.B.: Set your credentials in environment variables APP_ID and APP_KEY,
# either once via setenv or on the command line as in
# APP_ID=my-id APP_KEY=my-key python3 simple.py 
#

env = os.environ

default_headers = {
    'app_id': env.get('APP_ID', '你的id'),
    'app_key': env.get('APP_KEY', '你的key'),
    'Content-type': 'application/json'
}

service = 'https://api.mathpix.com/v3/latex'

#
# Return the base64 encoding of an image with the given filename.
#
def image_uri(filename):
    image_data = open(filename, "rb").read()
    return "data:image/jpg;base64," + base64.b64encode(image_data).decode()

#
# Call the Mathpix service with the given arguments, headers, and timeout.
#
def latex(args, headers=default_headers, timeout=30):
    r = requests.post(service,
        data=json.dumps(args), headers=headers, timeout=timeout)
    return json.loads(r.text)


def mathpix_clipboard(): # 识别剪贴板公式
    im = ImageGrab.grabclipboard()
    im.save('equa.png','PNG')
    r = latex({
        'src': image_uri("equa.png"),
        'formats': ['latex_simplified']
    })
    print(r['latex_simplified'])

if __name__ == '__main__':
    mathpix_clipboard()
    

PHP操作Excel

安装

composer require phpoffice/phpspreadsheet

简单使用

use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

$spreadsheet = new Spreadsheet();

for($i=1;$i<=3;$i++)
{
        if($i > 1)
        {
                $sheet = $spreadsheet->createSheet();
        }
        $spreadsheet->setActiveSheetIndex($i-1);
        $sheet = $spreadsheet->getActiveSheet();
        $sheet->setTitle('sheet_sheet'.$i);
        $sheet->setCellValue('A1', 'Hello World !'.$i);
}


$writer = new Xlsx($spreadsheet);
$writer->save('./public/hello world.xlsx');

基于PSCWS4词库的PHP中文分词工具

基于PSCWS4的PHP中文分词工具词库官方网址:http://www.xunsearch.com/scws/

这是用纯 PHP 代码实现的 C 版 Libscws 的全部功能,即第四版的 PSCWSPSCWS4

使用文档:http://www.xunsearch.com/scws/docs.php#pscws4

安装composer require wxkxklmyt/pscws

安装

composer require wxkxklmyt/pscws4

使用

    /**
     * SCWS中文分词
     *
     * @param string $text 分词字符串
     * @param number $number 权重高的词数量(默认5个)
     * @param string $type 返回类型,默认字符串
     * @param string $delimiter 分隔符
     * @return string|array 字符串|数组
     */
    $scws = new Scws();
    $scws -> scws('能做到柔和、善解、忍辱,才有办法与人结好缘。——证严法师《静思语》');

运行结果

静思 善解 法师 柔和 做到

其它说明

  1. 默认返回结果为字符串,返回字符串支持自定义分隔符,默认为空格
  2. 支持返回结果为数组
  3. 支持返回词数量自定义,默认为5个

mpdf7 中文乱码 解决办法

<?php
require_once './vendor/autoload.php';
//report errors
error_reporting(E_ALL);
ini_set("display_errors", 1);

$config = [
    'mode' => '+aCJK', 
    // "allowCJKoverflow" => true, 
    "autoScriptToLang" => true,
    // "allow_charset_conversion" => false,
    "autoLangToFont" => true,
];
$mpdf=new \Mpdf\Mpdf($config);

$mpdf->WriteHTML('Hello World 中文');
$mpdf->Output();

文档转换

1.使用phpword 实现word->pdf

require_once("includes/PHPWord/vendor/autoload.php");

$inputfile = "files/temp/offerte_Hankie-Pankie.docx";

$path = realpath(realpath(__DIR__) . '/includes/dompdf');

echo "realpath: " . $path;

\PhpOffice\PhpWord\Settings::setPdfRendererPath($path);
\PhpOffice\PhpWord\Settings::setPdfRendererName(\PhpOffice\PhpWord\Settings::PDF_RENDERER_DOMPDF);

//Load temp file
$phpWord = \PhpOffice\PhpWord\IOFactory::load($inputfile); 

//Save it
$xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord , 'PDF');
$xmlWriter->save('result.pdf');

2.Html2word

https://packagist.org/packages/cshaptx4869/html2word

3.word->html

https://segmentfault.com/a/1190000019821351?utm_source=tag-newest

4.PDFdo:

http://www.pdfdo.com/doc-to-pdf.aspx

5.cshaptx4869/html2word

https://packagist.org/packages/cshaptx4869/html2word

PHP实现二维数组(或多维数组)转换成一维数组的常见方法总结

本文实例总结了PHP实现二维数组(或多维数组)转换成一维数组的常见方法。分享给大家供大家参考,具体如下:

假设有下面一个二维数组:

$user = array(
  '0' => array('id' => 100, 'username' => 'a1'),
  '1' => array('id' => 101, 'username' => 'a2'),
  '2' => array('id' => 102, 'username' => 'a3'),
  '3' => array('id' => 103, 'username' => 'a4'),
  '4' => array('id' => 104, 'username' => 'a5'),
);

现在要转换成一维数组,有两种情况:

一种是将指定列转换成一维数组,这在另一篇文章有总结:PHP提取多维数组指定一列的方法总结

现在我们重点讲第二种情况,就是把所有的值都转换成一维数组,而且键值相同不会被覆盖,转换后的一维数组是这样的:

$result = array(100, 'a1', 101, 'a2', 102, 'a3', 103, 'a4', 104, 'a5');

主要有下面几个方法。

1 array_reduce函数法

用array_reduce()函数是较为快捷的方法:?

$result = array_reduce($user, function ($result, $value) {
  return array_merge($result, array_values($value));
}, array())

因为array_merge函数会把相同字符串键名的数组覆盖合并,所以必须先用array_value取出值后再合并。

如果第二维是数字键名,如:

$user = array(
  'a' => array(100, 'a1'),
  'b' => array(101, 'a2'),
  'c' => array(102, 'a3'),
  'd' => array(103, 'a4'),
  'e' => array(104, 'a5'),
);

那么直接这样就可以了:

$result = array_reduce($user, 'array_merge', array())

2 array_walk_recursive函数法

用array_walk_recursive()函数就非常灵活,可以把任意维度的数组转换成一维数组。

$result = [];
array_walk_recursive($user, function($value) use (&$result) {
  array_push($result, $value);
});

例如,下面这个多维数组:

$user4 = array(
  'a' => array(100, 'a1'),
  'b' => array(101, 'a2'),
  'c' => array(
    'd' => array(102, 'a3'),
    'e' => array(103, 'a4'),
  ),
);

用这个方法后就变成:

$result = array(100, 'a1', 101, 'a2', 102, 'a3', 103, 'a4');

3 array_map函数法

用array_map和array_reduce函数的方法类似,如下:

$result = [];
array_map(function ($value) use (&$result) {
  $result = array_merge($result, array_values($value));
}, $user);

只是需要多声明一个空的$result数组。

另外,也可以用array_walk的方法,和foreach循环的方法,原理和上面一样。

curl之采集QQ空间留言

主要流程解析

  • 首先,打开浏览器登录QQ空间并访问留言列表
  • 由于QQ空间的链接是https,curl方式请求https链接需要突破https认证,这就需要下载cacert.pem,下面请看cacert.pem的部署步骤
1、将下载的`cacert.pem`文件放至php安装目录下,比如:"D:\wamp\bin\php\php5.5.12";
2、在php.ini文件末尾追加以下配置

curl.cainfo="D:/wamp/bin/php/php5.5.12/cacert.pem"
openssl.cafile="D:/wamp/bin/php/php5.5.12/cacert.pem"
3、重启wamp服务器
4、上面步骤操作完毕,即可用curl方式访问QQ空间数据了

注意事项

  • 1、一定要设置时区为中国时区(避免cookie不同时区而自动过期)
  • 2、在PHP 的命令行模式下执行(避免超时问题)

扩展

通过QQ空间留言的采集,可以扩展下,比如采集空间日志、说说、图片,再比如,可以尝试刷留言(最好定时执行刷留言)。

完整代码示例

<?php

date_default_timezone_set('PRC');
//内存无限制
ini_set('limit_memory', -1);
//代码执行不超时
set_time_limit(0);

//爬取QQ空间留言url
$baseUrl = "https://user.qzone.qq.com/proxy/domain/m.qzone.qq.com/cgi-bin/new/get_msgb?";

////查找留言下一页规律
//第一页
//uin=928692509&hostUin=928692509&start=0&s=0.4852990803750179&format=jsonp&num=10&inCharset=utf-8&outCharset=utf-8&g_tk=1467544760&qzonetoken=e1fa4e2c8b873a4bf33b86e6f4838942ed7a538c10b5b8cb52e2bf4a959d253f90691c5ed8b045ada7&g_tk=1467544760
//第二页
//uin=928692509&hostUin=928692509&start=10&s=0.4852990803750179&format=jsonp&num=10&inCharset=utf-8&outCharset=utf-8&g_tk=1467544760&qzonetoken=e1fa4e2c8b873a4bf33b86e6f4838942ed7a538c10b5b8cb52e2bf4a959d253f90691c5ed8b045ada7&g_tk=1467544760

//爬取QQ空间留言(自己或QQ好友)
$hostUinArr = [
        '1270612806'=>'飞得更高', '928692509'=>'Burning', '2075425089'=>'怒放的生命'
    ];

//遍历采集QQ好友空间留言
foreach($hostUinArr as $hostUin=>$qqName) {
    downloadHostUinMessage($baseUrl, $hostUin, $qqName);
}

//以好友名称为目录,将下载下来的留言单独存放
function downloadHostUinMessage($baseUrl, $hostUin, $qqName){
    $qqName = iconv('UTF-8', 'GBK', $qqName);
    $dir = './qqMessage/'.$qqName;
    
    if(!is_dir($dir)) {
        mkdir($dir, 0777, true);
    }
    
    $totalPages = 1;
    
    for($i=0; $i<$totalPages; $i++){
        $start = $i*10;
        $params = "uin=928692509&hostUin={$hostUin}&start={$start}&s=0.4852990803750179&format=jsonp&num=10&inCharset=utf-8&outCharset=utf-8&g_tk=1467544760&qzonetoken=e1fa4e2c8b873a4bf33b86e6f4838942ed7a538c10b5b8cb52e2bf4a959d253f90691c5ed8b045ada7&g_tk=1467544760";
        
        $url = $baseUrl . $params;
        $iContent = qqCommentCurl($url);
        
        $iContent = str_replace('_Callback(', '', $iContent);
        $iContent = trim(substr($iContent, 0, -2));
        //计算总页数,保证留言采集完
        if($totalPages == 1) {
            $iContentObj = json_decode($iContent);
            if(isset($iContentObj->code) && $iContentObj->code == 0) {
                $dataObj = $iContentObj->data;
                $totalPages = ceil($dataObj->total/10);
            } else {
                //结束
                return false;
            }
        }
        
        file_put_contents($dir.'/'.$hostUin.'_'.$i.'.txt', $iContent);
    }
}

/**
 * @author RenZhicai
 * QQ空间留言请求curl
 * @param type $url
 * @return type
 */
function qqCommentCurl($url='')
{
    $ch = curl_init();
    //需要获取的 URL 地址,也可以在curl_init() 初始化会话的时候。
    curl_setopt($ch, CURLOPT_URL, $url);
    //TRUE 将curl_exec()获取的信息以字符串返回,而不是直接输出。
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    if(preg_match('/http:\/\//', $url)) {
        //FALSE 禁止 cURL 验证对等证书(peer's certificate)。要验证的交换证书可以在 CURLOPT_CAINFO 选项中设置,或在 CURLOPT_CAPATH中设置证书目录。
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //这个是重点。
    }
    
    //启用时会将头文件的信息作为数据流输出。
    curl_setopt($ch, CURLOPT_HEADER, 0); 
    //TRUE 时将会根据服务器返回 HTTP 头中的 "Location: " 重定向。(注意:这是递归的,"Location: " 发送几次就重定向几次,除非设置了 CURLOPT_MAXREDIRS,限制最大重定向次数。)。
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // 这样能够让cURL支持页面链接跳转

    //header头信息
    $headerArr = [
        "accept-language: zh-CN,zh;q=0.8",
        "user-agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"
    ];
    //设置 HTTP 头字段的数组。格式: array('Content-type: text/plain', 'Content-length: 100')
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headerArr);
    
    curl_setopt($ch, CURLOPT_AUTOREFERER, true);
    
    //设置新的cookie,忽略之前会话时存的cookie
    curl_setopt($ch, CURLOPT_COOKIESESSION, TRUE);
    //包含 cookie 数据的文件名
    curl_setopt($ch, CURLOPT_COOKIEFILE, 'daoyancookiefile');
    //连接结束后,比如,调用 curl_close 后,保存 cookie 信息的文件。
    curl_setopt($ch, CURLOPT_COOKIEJAR, 'daoyancookiefile');

    //登录后的cookie信息  
    $cookieStr = "cookie: pgv_pvi=9803880448; tvfe_boss_uuid=7aa8e6046d68c6a2; RK=qBcndcbOTD; eas_sid=K1s5q0V8H8N5B4c1j9e2L0D8W3; pgv_pvid=8596381360; o_cookie=928692509; __Q_w_s_hat_seed=1; __Q_w_s__QZN_TodoMsgCnt=1; ptcz=dab5485d33e04f8612e84673ce0609bdcae13f792156f92dccf5a9784988a16a; uin=o0928692509; skey=@CgmH4oeQ8; pt2gguin=o0928692509; p_uin=o0928692509; pt4_token=Lub2nMWbTLfbx0TYxH*uyDBOU4tEBXvKvv0JNQRQuGo_; p_skey=MxE5cMJA2xHQ8BXgpbVqSL3hgeoEfcq-quN1kvcUrcQ_; pgv_info=ssid=s7150557986; Loading=Yes; qz_screen=1366x768; qqmusic_uin=; qqmusic_key=; qqmusic_fromtag=; qzmusicplayer=qzone_player_928692509_1509368391144; QZ_FE_WEBP_SUPPORT=1; cpu_performance_v8=6";
    //设定 HTTP 请求中"Cookie: "部分的内容。多个 cookie 用分号分隔,分号后带一个空格(例如, "fruit=apple; colour=red")。
    curl_setopt($ch, CURLOPT_COOKIE, $cookieStr);

    $output = curl_exec($ch);
    if($output === false){
                //记录curl请求失败的详情
        $errorInfo = "cURL Error: ".curl_error($ch);
        file_put_contents('curl_error_'.date('YmdHis').'.txt', $errorInfo);
    }
    
    curl_close($ch);

    return $output;
}

采集效果一览

PHP date_format

面向对象风格 <?php
$date = new DateTime('2000-01-01');
echo $date->format('Y-m-d H:i:s');
?>

过程化风格 <?php
$date = date_create('2000-01-01');
echo date_format($date, 'Y-m-d H:i:s');
?>

以上例程会输出:

2000-01-01 00:00:00