Skip to content

Instantly share code, notes, and snippets.

@shtrih
Last active February 19, 2020 20:59
Show Gist options
  • Save shtrih/171a5383cbf0220bd71cee6b6a1fc755 to your computer and use it in GitHub Desktop.
Save shtrih/171a5383cbf0220bd71cee6b6a1fc755 to your computer and use it in GitHub Desktop.
<?php
/**
* Created by PhpStorm.
* User: shtrih
* Date: 24.09.2017
* Time: 21:57
*/
namespace App\Util;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
trait Csv
{
/**
* @param \Iterator|array $rows
* @param $filepath
* @param callable|null $rowPreSaveCallback Row as argument
*/
public function saveCSV($rows, $filepath, callable $rowPreSaveCallback = null)
{
/**
* @var \Illuminate\Console\OutputStyle $this->output
*/
$progressbar = $this->output->createProgressBar(count($rows));
$file = fopen($filepath, 'w');
foreach ($rows as $row) {
if ($rowPreSaveCallback) {
$row = call_user_func($rowPreSaveCallback, $row);
if (!empty($row['rows'])) {
foreach ($row['rows'] as $r) {
fputcsv($file, $r);
}
continue;
}
}
fputcsv($file, $row);
$progressbar->advance();
}
fclose($file);
$progressbar->finish();
}
public function readCSV($filename)
{
$result = [];
$h = fopen($filename, 'r');
if (false === $h) {
throw new FileNotFoundException();
}
while (($row = fgetcsv($h)) !== false) {
$result[] = $row;
}
return $result;
}
}
<?php
namespace App\Console\Commands;
use App\Util\Csv;
use GuzzleHttp\Client;
use Illuminate\Console\Command;
class GrabGameList extends Command
{
use Csv;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'steam:game-list';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Получаем список игр с https://steamdb.info/sales/?max_price=25&min_discount=0&min_rating=0&displayOnly=Game&category=29';
/**
* @var Client
*/
private $webClient;
/**
* Create a new command instance.
*
* @param Client $webClient
*/
public function __construct(Client $webClient)
{
parent::__construct();
$this->webClient = $webClient;
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->info('Fetching games');
$fromCache = true;
$html = \Cache::remember('games-list', 30, function () use (&$fromCache) {
$fromCache = false;
$response = $this->webClient->get('https://steamdb.info/sales/', ['query' => [
'max_price' => 25,
'min_discount' => 0,
'min_rating' => 0,
'displayOnly' => 'Game',
'category' => 29,
'cc' => 'ru',
]]);
return $response->getBody()->getContents();
});
$this->info(sprintf('Fetched from %s', $fromCache ? 'cache' : 'host'));
// dd($html);
$query = htmlqp($html, 'table > tbody', ['convert_to_encoding' => 'utf-8'])
->find('tr')
->sort(function (\DOMNode $a, \DOMNode $b) {
$a = qp($a)->find('td:nth(5)')->attr('data-sort');
$b = qp($b)->find('td:nth(5)')->attr('data-sort');
if ($a === $b)
return 0;
return $a > $b ? 1 : -1;
}, true);
$file = storage_path('app\games-list.csv');
$this->saveCSV($query, $file, function ($row) {
return [$row->attr('data-appid'), $row->find('td:nth(3) a')->text(), $row->find('td:nth(5)')->text()];
});
$this->info('');
$this->info('Saved to csv into ' . $file);
}
}
<?php
namespace App\Console\Commands;
use App\Util\Csv;
use GuzzleHttp\Client;
use Illuminate\Console\Command;
class GrabItemsByGame extends Command
{
use Csv;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'steam:items-list';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* @var Client
*/
private $webClient;
/**
* Create a new command instance.
*
* @param Client $webClient
*/
public function __construct(Client $webClient)
{
parent::__construct();
$this->webClient = $webClient;
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$gamesList = $this->readCSV(storage_path('app\games-list.csv'));
$this->output->progressStart(count($gamesList));
foreach ($gamesList as &$game) {
list($appid, $name) = $game;
if (!$name) {
$this->warn('Empty game name. Skipping.');
continue;
}
$sleep = 29 + mt_rand(10,23);
$this->info(sprintf('Fetching «%s», sleep %d.', $name, $sleep));
$cacheKey = 'game-items-' . $appid;
$item = \Cache::get($cacheKey);
$cached = true;
if (!$item) {
$cached = false;
sleep($sleep);
// http://steamcommunity.com/market/search/render/?query=Masked%20Shooters%202&start=0&count=50&search_descriptions=0&sort_column=price&sort_dir=desc
// appid=753&category_753_Game%5B%5D=tag_app_585690
$response = $this->webClient->get('http://steamcommunity.com/market/search/render/', [
'query' => [
// 'query' => $name,
'start' => 0,
'count' => 50,
'search_descriptions' => 0,
'sort_column' => 'price',
'sort_dir' => 'desc',
'appid' => 753,
'category_753_Game[]' => 'tag_app_' . $appid
],
'headers' => [
'Accept-Language' => 'en-US;q=0.8,en;q=0.6',
],
'cookie' => [
'steamCountry' => 'RU%7C67b95f7bc66bd19244aab3e0c8e93d89'
]
]);
$item = \GuzzleHttp\json_decode($response->getBody()->getContents(), true);
$cacheTime = 60 * 12;
if (empty($item['success'])) {
$cacheTime = 30;
$this->warn('Failed to get items: ' . $response->getReasonPhrase());
}
\Cache::put($cacheKey, $item, $cacheTime);
}
$game['items-html'] = $item['results_html'];
$this->output->progressAdvance();
}
$this->output->progressFinish();
libxml_use_internal_errors(true); // issues with clone nodes with id attr
$this->saveCSV($gamesList, storage_path('app\games-items-list.csv'), function ($row) {
if (!empty($row['items-html'])) {
$items = [];
$query = htmlqp($row['items-html'], '.market_listing_row', ['convert_to_encoding' => 'utf-8']);
foreach ($query as $item) {
$items[] = ['', $item->find('span.market_listing_item_name')->text(), $item->find('span.normal_price > .normal_price')->text()];
}
unset($row['items-html']);
array_unshift($items, $row);
return [
'rows' => $items
];
}
return $row;
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment